You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e59dca192 | |||
| 1109d53720 | |||
| f12363a5b7 | |||
| 7d4ffec74e | |||
| 281fede58c | |||
| edf152485b | |||
| 18a1bfbe90 | |||
| 7c32bbfd2d | |||
| 4eae206b64 | |||
| c9a47b1fac | |||
| 7624d3fbc3 | |||
| e9129576a9 |
@@ -0,0 +1,26 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `habbo_mentions` (
|
||||||
|
`id` INT NOT NULL AUTO_INCREMENT,
|
||||||
|
`target_user_id` INT NOT NULL,
|
||||||
|
`sender_user_id` INT NOT NULL,
|
||||||
|
`sender_username` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
`room_id` INT NOT NULL,
|
||||||
|
`room_name` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
|
`message` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
|
`mention_type` TINYINT NOT NULL DEFAULT 0,
|
||||||
|
`timestamp` INT NOT NULL DEFAULT 0,
|
||||||
|
`read` TINYINT NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_target_read` (`target_user_id`, `read`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
INSERT IGNORE INTO `emulator_settings` (`key`, `value`) VALUES
|
||||||
|
('mentions.enabled', '1'),
|
||||||
|
('mentions.room.aliases', 'amici,friends,all,everyone,tutti,room,stanza'),
|
||||||
|
('mentions.max.targets', '50'),
|
||||||
|
('mentions.cooldown.ms', '3000'),
|
||||||
|
('mentions.store.limit', '50');
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE `wordfilter`
|
||||||
|
ADD COLUMN `prefix_only` ENUM('0','1') NOT NULL DEFAULT '0'
|
||||||
|
COMMENT 'When 1, this word only applies to custom prefixes, not to chat/motto/guild.' AFTER `mute`;
|
||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.eu.habbo</groupId>
|
<groupId>com.eu.habbo</groupId>
|
||||||
<artifactId>Habbo</artifactId>
|
<artifactId>Habbo</artifactId>
|
||||||
<version>4.2.30</version>
|
<version>4.2.32</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.eu.habbo.habbohotel.campaign.calendar.CalendarManager;
|
|||||||
import com.eu.habbo.habbohotel.catalog.CatalogManager;
|
import com.eu.habbo.habbohotel.catalog.CatalogManager;
|
||||||
import com.eu.habbo.habbohotel.wheel.WheelManager;
|
import com.eu.habbo.habbohotel.wheel.WheelManager;
|
||||||
import com.eu.habbo.habbohotel.soundboard.SoundboardManager;
|
import com.eu.habbo.habbohotel.soundboard.SoundboardManager;
|
||||||
|
import com.eu.habbo.habbohotel.mentions.MentionManager;
|
||||||
import com.eu.habbo.habbohotel.commands.CommandHandler;
|
import com.eu.habbo.habbohotel.commands.CommandHandler;
|
||||||
import com.eu.habbo.habbohotel.crafting.CraftingManager;
|
import com.eu.habbo.habbohotel.crafting.CraftingManager;
|
||||||
import com.eu.habbo.habbohotel.guides.GuideManager;
|
import com.eu.habbo.habbohotel.guides.GuideManager;
|
||||||
@@ -68,6 +69,7 @@ public class GameEnvironment {
|
|||||||
private InfostandBackgroundManager infostandBackgroundManager;
|
private InfostandBackgroundManager infostandBackgroundManager;
|
||||||
private WheelManager wheelManager;
|
private WheelManager wheelManager;
|
||||||
private SoundboardManager soundboardManager;
|
private SoundboardManager soundboardManager;
|
||||||
|
private MentionManager mentionManager;
|
||||||
|
|
||||||
public void load() throws Exception {
|
public void load() throws Exception {
|
||||||
LOGGER.info("GameEnvironment -> Loading...");
|
LOGGER.info("GameEnvironment -> Loading...");
|
||||||
@@ -99,6 +101,7 @@ public class GameEnvironment {
|
|||||||
this.infostandBackgroundManager = new InfostandBackgroundManager();
|
this.infostandBackgroundManager = new InfostandBackgroundManager();
|
||||||
this.wheelManager = new WheelManager();
|
this.wheelManager = new WheelManager();
|
||||||
this.soundboardManager = new SoundboardManager();
|
this.soundboardManager = new SoundboardManager();
|
||||||
|
this.mentionManager = new MentionManager();
|
||||||
|
|
||||||
this.roomManager.loadPublicRooms();
|
this.roomManager.loadPublicRooms();
|
||||||
this.navigatorManager.loadNavigator();
|
this.navigatorManager.loadNavigator();
|
||||||
@@ -202,6 +205,10 @@ public class GameEnvironment {
|
|||||||
return this.petManager;
|
return this.petManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MentionManager getMentionManager() {
|
||||||
|
return this.mentionManager;
|
||||||
|
}
|
||||||
|
|
||||||
public AchievementManager getAchievementManager() {
|
public AchievementManager getAchievementManager() {
|
||||||
return this.achievementManager;
|
return this.achievementManager;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.eu.habbo.Emulator;
|
|||||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||||
import com.eu.habbo.habbohotel.modtool.WordFilter;
|
import com.eu.habbo.habbohotel.modtool.WordFilter;
|
||||||
import com.eu.habbo.habbohotel.modtool.WordFilterWord;
|
import com.eu.habbo.habbohotel.modtool.WordFilterWord;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.RoomChatMessageBubbles;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -21,30 +22,44 @@ public class FilterWordCommand extends Command {
|
|||||||
@Override
|
@Override
|
||||||
public boolean handle(GameClient gameClient, String[] params) throws Exception {
|
public boolean handle(GameClient gameClient, String[] params) throws Exception {
|
||||||
if (params.length < 2) {
|
if (params.length < 2) {
|
||||||
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_filterword.missing_word"));
|
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_filterword.missing_word"), RoomChatMessageBubbles.ALERT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String word = params[1];
|
String word = params[1];
|
||||||
|
|
||||||
|
// Optional trailing "prefix" keyword marks the word as prefix-only (blocks
|
||||||
|
// custom prefixes but not chat/motto/guild). Usage:
|
||||||
|
// :filterword <word> -> everywhere, default replacement
|
||||||
|
// :filterword <word> <replacement> -> everywhere
|
||||||
|
// :filterword <word> prefix -> prefix-only, default replacement
|
||||||
|
// :filterword <word> <replacement> prefix -> prefix-only
|
||||||
|
boolean prefixOnly = false;
|
||||||
String replacement = WordFilter.DEFAULT_REPLACEMENT;
|
String replacement = WordFilter.DEFAULT_REPLACEMENT;
|
||||||
if (params.length == 3) {
|
|
||||||
replacement = params[2];
|
if (params.length >= 3) {
|
||||||
|
if (params[params.length - 1].equalsIgnoreCase("prefix")) {
|
||||||
|
prefixOnly = true;
|
||||||
|
if (params.length >= 4) replacement = params[2];
|
||||||
|
} else {
|
||||||
|
replacement = params[2];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WordFilterWord wordFilterWord = new WordFilterWord(word, replacement);
|
WordFilterWord wordFilterWord = new WordFilterWord(word, replacement, prefixOnly);
|
||||||
|
|
||||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO wordfilter (`key`, `replacement`) VALUES (?, ?)")) {
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO wordfilter (`key`, `replacement`, `prefix_only`) VALUES (?, ?, ?)")) {
|
||||||
statement.setString(1, word);
|
statement.setString(1, word);
|
||||||
statement.setString(2, replacement);
|
statement.setString(2, replacement);
|
||||||
|
statement.setString(3, prefixOnly ? "1" : "0");
|
||||||
statement.execute();
|
statement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
LOGGER.error("Caught SQL exception", e);
|
LOGGER.error("Caught SQL exception", e);
|
||||||
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_filterword.error"));
|
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_filterword.error"), RoomChatMessageBubbles.ALERT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_filterword.added").replace("%word%", word).replace("%replacement%", replacement));
|
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_filterword.added").replace("%word%", word).replace("%replacement%", replacement) + (prefixOnly ? " [prefix-only]" : ""), RoomChatMessageBubbles.ALERT);
|
||||||
Emulator.getGameEnvironment().getWordFilter().addWord(wordFilterWord);
|
Emulator.getGameEnvironment().getWordFilter().addWord(wordFilterWord);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package com.eu.habbo.habbohotel.mentions;
|
||||||
|
|
||||||
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
public class HabboMention {
|
||||||
|
|
||||||
|
public static final int TYPE_DIRECT = 0;
|
||||||
|
public static final int TYPE_ROOM = 1;
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final int targetUserId;
|
||||||
|
private final int senderUserId;
|
||||||
|
private final String senderUsername;
|
||||||
|
private final int roomId;
|
||||||
|
private final String roomName;
|
||||||
|
private final String message;
|
||||||
|
private final int mentionType;
|
||||||
|
private final int timestamp;
|
||||||
|
private final boolean read;
|
||||||
|
|
||||||
|
public HabboMention(ResultSet set) throws SQLException {
|
||||||
|
this.id = set.getInt("id");
|
||||||
|
this.targetUserId = set.getInt("target_user_id");
|
||||||
|
this.senderUserId = set.getInt("sender_user_id");
|
||||||
|
this.senderUsername = set.getString("sender_username");
|
||||||
|
this.roomId = set.getInt("room_id");
|
||||||
|
this.roomName = set.getString("room_name");
|
||||||
|
this.message = set.getString("message");
|
||||||
|
this.mentionType = set.getInt("mention_type");
|
||||||
|
this.timestamp = set.getInt("timestamp");
|
||||||
|
this.read = set.getInt("read") == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HabboMention(int targetUserId, int id, Habbo sender, Room room, String roomName, String message, int mentionType, int timestamp) {
|
||||||
|
this.id = id;
|
||||||
|
this.targetUserId = targetUserId;
|
||||||
|
this.senderUserId = sender.getHabboInfo().getId();
|
||||||
|
this.senderUsername = sender.getHabboInfo().getUsername();
|
||||||
|
this.roomId = room.getId();
|
||||||
|
this.roomName = roomName;
|
||||||
|
this.message = message;
|
||||||
|
this.mentionType = mentionType;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.read = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTargetUserId() {
|
||||||
|
return this.targetUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSenderUserId() {
|
||||||
|
return this.senderUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderUsername() {
|
||||||
|
return this.senderUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRoomId() {
|
||||||
|
return this.roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoomName() {
|
||||||
|
return this.roomName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMentionType() {
|
||||||
|
return this.mentionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimestamp() {
|
||||||
|
return this.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRead() {
|
||||||
|
return this.read;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,468 @@
|
|||||||
|
package com.eu.habbo.habbohotel.mentions;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.messenger.MessengerBuddy;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.Room;
|
||||||
|
import com.eu.habbo.habbohotel.rooms.RoomChatType;
|
||||||
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
|
import com.eu.habbo.habbohotel.users.HabboManager;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class MentionManager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(MentionManager.class);
|
||||||
|
|
||||||
|
private static final int ROOM_NAME_MAX_LENGTH = 64;
|
||||||
|
private static final int MESSAGE_MAX_LENGTH = 255;
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Integer, Long> cooldowns = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<Integer, Long> roomBroadcastCooldowns = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// Per-user request rate limits for the incoming packets that hit the DB.
|
||||||
|
private final ConcurrentHashMap<Integer, Long> requestListCooldowns = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<Integer, Long> markReadCooldowns = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<Integer, Long> markAllCooldowns = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<Integer, Long> deleteCooldowns = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private volatile long lastPrune = System.currentTimeMillis();
|
||||||
|
private static final long PRUNE_INTERVAL_MS = 5 * 60_000L;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return Emulator.getConfig().getInt("mentions.enabled", 1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Broadcast category resolved from a mention alias. */
|
||||||
|
public enum BroadcastScope {
|
||||||
|
NONE,
|
||||||
|
// @room / @stanza - reaches the people currently in the room.
|
||||||
|
ROOM,
|
||||||
|
// @friends / @amici - reaches the sender's online friends, requires acc_mention_friends.
|
||||||
|
FRIENDS,
|
||||||
|
// @all / @everyone / @tutti - reaches every online user, requires acc_mention_everyone.
|
||||||
|
EVERYONE
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Permission key (DB column) required to send an "everyone" broadcast. */
|
||||||
|
public static final String PERMISSION_EVERYONE = "acc_mention_everyone";
|
||||||
|
/** Permission key (DB column) required to send a "friends" broadcast. */
|
||||||
|
public static final String PERMISSION_FRIENDS = "acc_mention_friends";
|
||||||
|
|
||||||
|
private Set<String> parseAliases(String configKey, String defaultValue) {
|
||||||
|
Set<String> aliases = new HashSet<>();
|
||||||
|
String raw = Emulator.getConfig().getValue(configKey, defaultValue);
|
||||||
|
for (String alias : raw.split(",")) {
|
||||||
|
String trimmed = alias.trim().toLowerCase();
|
||||||
|
if (!trimmed.isEmpty()) {
|
||||||
|
aliases.add(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> roomAliases() {
|
||||||
|
return parseAliases("mentions.room.aliases", "room,stanza");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> friendsAliases() {
|
||||||
|
return parseAliases("mentions.friends.aliases", "friends,amici");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> everyoneAliases() {
|
||||||
|
return parseAliases("mentions.everyone.aliases", "all,everyone,tutti");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classify an alias candidate (lowercased, punctuation-trimmed) into a
|
||||||
|
* broadcast scope. {@link BroadcastScope#EVERYONE} wins over
|
||||||
|
* {@link BroadcastScope#FRIENDS} which wins over {@link BroadcastScope#ROOM}
|
||||||
|
* so an admin who's also configured the same word into two lists gets the
|
||||||
|
* most permissive scope (which is also the one requiring the strongest
|
||||||
|
* permission, so it can't be misused).
|
||||||
|
*/
|
||||||
|
private BroadcastScope classifyAlias(String alias,
|
||||||
|
Set<String> everyone,
|
||||||
|
Set<String> friends,
|
||||||
|
Set<String> room) {
|
||||||
|
if (alias.isEmpty()) return BroadcastScope.NONE;
|
||||||
|
if (everyone.contains(alias)) return BroadcastScope.EVERYONE;
|
||||||
|
if (friends.contains(alias)) return BroadcastScope.FRIENDS;
|
||||||
|
if (room.contains(alias)) return BroadcastScope.ROOM;
|
||||||
|
return BroadcastScope.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(Habbo sender, Room room, String message, RoomChatType type) {
|
||||||
|
try {
|
||||||
|
if (!this.isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender == null || room == null || message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.isEmpty() || message.indexOf('@') < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int senderId = sender.getHabboInfo().getId();
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long cooldownMs = Emulator.getConfig().getInt("mentions.cooldown.ms", 3000);
|
||||||
|
Long last = this.cooldowns.get(senderId);
|
||||||
|
if (last != null && (now - last) < cooldownMs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> roomAliases = this.roomAliases();
|
||||||
|
Set<String> friendsAliases = this.friendsAliases();
|
||||||
|
Set<String> everyoneAliases = this.everyoneAliases();
|
||||||
|
|
||||||
|
BroadcastScope broadcastScope = BroadcastScope.NONE;
|
||||||
|
LinkedHashSet<String> directTokens = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
for (String token : message.split("\\s+")) {
|
||||||
|
if (token.length() < 2 || token.charAt(0) != '@') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String raw = token.substring(1);
|
||||||
|
String aliasCandidate = trimTrailingPunctuation(raw).toLowerCase();
|
||||||
|
|
||||||
|
BroadcastScope scope = this.classifyAlias(aliasCandidate, everyoneAliases, friendsAliases, roomAliases);
|
||||||
|
|
||||||
|
if (scope != BroadcastScope.NONE) {
|
||||||
|
// Promote to the strongest detected scope so a message with
|
||||||
|
// both @room and @all routes through the @all permission.
|
||||||
|
if (scope.ordinal() > broadcastScope.ordinal()) {
|
||||||
|
broadcastScope = scope;
|
||||||
|
}
|
||||||
|
} else if (!raw.isEmpty()) {
|
||||||
|
directTokens.add(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gate the broadcast on the matching permission. If the sender does
|
||||||
|
// not have the right to use it, drop the broadcast entirely but
|
||||||
|
// keep processing any direct @nick tokens in the same message.
|
||||||
|
if (broadcastScope == BroadcastScope.EVERYONE && !sender.hasPermission(PERMISSION_EVERYONE)) {
|
||||||
|
broadcastScope = BroadcastScope.NONE;
|
||||||
|
} else if (broadcastScope == BroadcastScope.FRIENDS && !sender.hasPermission(PERMISSION_FRIENDS)) {
|
||||||
|
broadcastScope = BroadcastScope.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (broadcastScope == BroadcastScope.NONE && directTokens.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stricter cooldown for broadcasts: one @all/@friends/@room expands to
|
||||||
|
// up to mentions.max.targets DB writes and packet sends, so rate-limit it
|
||||||
|
// separately from direct mentions.
|
||||||
|
if (broadcastScope != BroadcastScope.NONE) {
|
||||||
|
long roomCooldownMs = Emulator.getConfig().getInt("mentions.room.cooldown.ms", 15000);
|
||||||
|
Long lastRoom = this.roomBroadcastCooldowns.get(senderId);
|
||||||
|
if (lastRoom != null && (now - lastRoom) < roomCooldownMs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxTargets = Emulator.getConfig().getInt("mentions.max.targets", 50);
|
||||||
|
if (maxTargets <= 0) maxTargets = 1;
|
||||||
|
|
||||||
|
// Bound the number of direct tokens we even attempt to resolve so a
|
||||||
|
// crafted message can't push us through the room iteration N times.
|
||||||
|
int maxDirectTokens = Math.min(directTokens.size(), maxTargets);
|
||||||
|
|
||||||
|
List<Habbo> targets = new ArrayList<>();
|
||||||
|
Set<Integer> seen = new HashSet<>();
|
||||||
|
|
||||||
|
switch (broadcastScope) {
|
||||||
|
case EVERYONE:
|
||||||
|
this.collectEveryoneTargets(senderId, targets, seen, maxTargets);
|
||||||
|
break;
|
||||||
|
case FRIENDS:
|
||||||
|
this.collectFriendsTargets(sender, senderId, targets, seen, maxTargets);
|
||||||
|
break;
|
||||||
|
case ROOM:
|
||||||
|
this.collectRoomTargets(room, senderId, targets, seen, maxTargets);
|
||||||
|
break;
|
||||||
|
case NONE:
|
||||||
|
default:
|
||||||
|
int processed = 0;
|
||||||
|
for (String token : directTokens) {
|
||||||
|
if (processed++ >= maxDirectTokens) break;
|
||||||
|
Habbo habbo = this.resolveHabbo(room, token);
|
||||||
|
if (habbo == null || habbo.getHabboInfo().getId() == senderId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (seen.add(habbo.getHabboInfo().getId())) {
|
||||||
|
targets.add(habbo);
|
||||||
|
}
|
||||||
|
if (targets.size() >= maxTargets) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targets.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cooldowns.put(senderId, now);
|
||||||
|
if (broadcastScope != BroadcastScope.NONE) this.roomBroadcastCooldowns.put(senderId, now);
|
||||||
|
this.pruneCooldownsIfDue(now);
|
||||||
|
|
||||||
|
int mentionType = (broadcastScope != BroadcastScope.NONE) ? HabboMention.TYPE_ROOM : HabboMention.TYPE_DIRECT;
|
||||||
|
int timestamp = Emulator.getIntUnixTimestamp();
|
||||||
|
String roomName = truncate(room.getName(), ROOM_NAME_MAX_LENGTH);
|
||||||
|
String storedMessage = truncate(message, MESSAGE_MAX_LENGTH);
|
||||||
|
|
||||||
|
for (Habbo target : targets) {
|
||||||
|
this.store(target, sender, room, storedMessage, mentionType, timestamp, roomName);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Failed to process mentions.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectRoomTargets(Room room, int senderId, List<Habbo> targets, Set<Integer> seen, int maxTargets) {
|
||||||
|
for (Habbo habbo : room.getHabbos()) {
|
||||||
|
if (habbo == null || habbo.getHabboInfo().getId() == senderId) continue;
|
||||||
|
if (seen.add(habbo.getHabboInfo().getId())) targets.add(habbo);
|
||||||
|
if (targets.size() >= maxTargets) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectFriendsTargets(Habbo sender, int senderId, List<Habbo> targets, Set<Integer> seen, int maxTargets) {
|
||||||
|
if (sender.getMessenger() == null) return;
|
||||||
|
HabboManager habboManager = Emulator.getGameEnvironment().getHabboManager();
|
||||||
|
for (MessengerBuddy buddy : sender.getMessenger().getFriends().values()) {
|
||||||
|
if (buddy == null) continue;
|
||||||
|
int buddyId = buddy.getId();
|
||||||
|
if (buddyId == senderId) continue;
|
||||||
|
Habbo online = habboManager.getHabbo(buddyId);
|
||||||
|
if (online == null) continue;
|
||||||
|
if (seen.add(buddyId)) targets.add(online);
|
||||||
|
if (targets.size() >= maxTargets) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectEveryoneTargets(int senderId, List<Habbo> targets, Set<Integer> seen, int maxTargets) {
|
||||||
|
for (Habbo habbo : Emulator.getGameEnvironment().getHabboManager().getOnlineHabbos().values()) {
|
||||||
|
if (habbo == null || habbo.getHabboInfo().getId() == senderId) continue;
|
||||||
|
if (seen.add(habbo.getHabboInfo().getId())) targets.add(habbo);
|
||||||
|
if (targets.size() >= maxTargets) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void store(Habbo target, Habbo sender, Room room, String message, int mentionType, int timestamp, String roomName) {
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement(
|
||||||
|
"INSERT INTO habbo_mentions (target_user_id, sender_user_id, sender_username, room_id, room_name, message, mention_type, timestamp, `read`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0)",
|
||||||
|
Statement.RETURN_GENERATED_KEYS)) {
|
||||||
|
statement.setInt(1, target.getHabboInfo().getId());
|
||||||
|
statement.setInt(2, sender.getHabboInfo().getId());
|
||||||
|
statement.setString(3, sender.getHabboInfo().getUsername());
|
||||||
|
statement.setInt(4, room.getId());
|
||||||
|
statement.setString(5, roomName);
|
||||||
|
statement.setString(6, message);
|
||||||
|
statement.setInt(7, mentionType);
|
||||||
|
statement.setInt(8, timestamp);
|
||||||
|
statement.executeUpdate();
|
||||||
|
|
||||||
|
int generatedId = 0;
|
||||||
|
try (ResultSet keys = statement.getGeneratedKeys()) {
|
||||||
|
if (keys.next()) {
|
||||||
|
generatedId = keys.getInt(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't push a notification to the client when the INSERT did not
|
||||||
|
// return an id - the client dedup keys on id and a 0 would skip
|
||||||
|
// dedup entirely, opening a flood path on the next packet.
|
||||||
|
if (generatedId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HabboMention mention = new HabboMention(target.getHabboInfo().getId(), generatedId, sender, room, roomName, message, mentionType, timestamp);
|
||||||
|
|
||||||
|
if (target.getClient() != null) {
|
||||||
|
target.getClient().sendResponse(new com.eu.habbo.messages.outgoing.mentions.MentionReceivedComposer(mention));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to store mention.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HabboMention> getMentions(int userId, int limit) {
|
||||||
|
List<HabboMention> mentions = new ArrayList<>();
|
||||||
|
if (limit <= 0) limit = 50;
|
||||||
|
if (limit > 200) limit = 200;
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement(
|
||||||
|
"SELECT * FROM habbo_mentions WHERE target_user_id = ? ORDER BY id DESC LIMIT ?")) {
|
||||||
|
statement.setInt(1, userId);
|
||||||
|
statement.setInt(2, limit);
|
||||||
|
try (ResultSet set = statement.executeQuery()) {
|
||||||
|
while (set.next()) {
|
||||||
|
mentions.add(new HabboMention(set));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to load mentions.", e);
|
||||||
|
}
|
||||||
|
return mentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markRead(int userId, int mode, int mentionId) {
|
||||||
|
// Caller has already validated mode and mentionId; this method is defensive only.
|
||||||
|
if (mode != 0 && mode != 1) return;
|
||||||
|
if (mode == 1 && mentionId <= 0) return;
|
||||||
|
|
||||||
|
String query = mode == 1
|
||||||
|
? "UPDATE habbo_mentions SET `read` = 1 WHERE target_user_id = ? AND id = ? AND `read` = 0"
|
||||||
|
: "UPDATE habbo_mentions SET `read` = 1 WHERE target_user_id = ? AND `read` = 0";
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement(query)) {
|
||||||
|
statement.setInt(1, userId);
|
||||||
|
if (mode == 1) {
|
||||||
|
statement.setInt(2, mentionId);
|
||||||
|
}
|
||||||
|
statement.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to mark mentions as read.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(int userId, int mentionId) {
|
||||||
|
if (mentionId <= 0) return;
|
||||||
|
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||||
|
PreparedStatement statement = connection.prepareStatement(
|
||||||
|
"DELETE FROM habbo_mentions WHERE target_user_id = ? AND id = ?")) {
|
||||||
|
statement.setInt(1, userId);
|
||||||
|
statement.setInt(2, mentionId);
|
||||||
|
statement.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Failed to delete mention.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-user rate limit for {@code RequestMentionsEvent}. Returns true when
|
||||||
|
* the caller should be served, false when it must be silently dropped.
|
||||||
|
*/
|
||||||
|
public boolean tryAcquireRequestList(int userId) {
|
||||||
|
long cooldownMs = Emulator.getConfig().getInt("mentions.request.cooldown.ms", 2000);
|
||||||
|
return tryAcquire(this.requestListCooldowns, userId, cooldownMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-user rate limit for {@code MarkMentionsReadEvent}. The mark-single
|
||||||
|
* path (mode == 1) is cheap and gets a short window; the mark-all path
|
||||||
|
* (mode != 1) is a bulk UPDATE and gets a longer one.
|
||||||
|
*/
|
||||||
|
public boolean tryAcquireMarkRead(int userId, int mode) {
|
||||||
|
long cooldownMs;
|
||||||
|
ConcurrentHashMap<Integer, Long> bucket;
|
||||||
|
if (mode == 1) {
|
||||||
|
cooldownMs = Emulator.getConfig().getInt("mentions.markread.cooldown.ms", 500);
|
||||||
|
bucket = this.markReadCooldowns;
|
||||||
|
} else {
|
||||||
|
cooldownMs = Emulator.getConfig().getInt("mentions.markall.cooldown.ms", 5000);
|
||||||
|
bucket = this.markAllCooldowns;
|
||||||
|
}
|
||||||
|
return tryAcquire(bucket, userId, cooldownMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-user rate limit for {@code DeleteMentionEvent}.
|
||||||
|
*/
|
||||||
|
public boolean tryAcquireDelete(int userId) {
|
||||||
|
long cooldownMs = Emulator.getConfig().getInt("mentions.delete.cooldown.ms", 500);
|
||||||
|
return tryAcquire(this.deleteCooldowns, userId, cooldownMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryAcquire(ConcurrentHashMap<Integer, Long> bucket, int userId, long cooldownMs) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Long last = bucket.get(userId);
|
||||||
|
if (last != null && (now - last) < cooldownMs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bucket.put(userId, now);
|
||||||
|
this.pruneCooldownsIfDue(now);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Periodically drop cooldown entries older than the largest window so the
|
||||||
|
* maps don't accumulate one entry per user-that-ever-played for the entire
|
||||||
|
* server lifetime.
|
||||||
|
*/
|
||||||
|
private void pruneCooldownsIfDue(long now) {
|
||||||
|
if (now - this.lastPrune < PRUNE_INTERVAL_MS) return;
|
||||||
|
this.lastPrune = now;
|
||||||
|
|
||||||
|
long mentionWindow = Emulator.getConfig().getInt("mentions.cooldown.ms", 3000);
|
||||||
|
long roomWindow = Emulator.getConfig().getInt("mentions.room.cooldown.ms", 15000);
|
||||||
|
long requestWindow = Emulator.getConfig().getInt("mentions.request.cooldown.ms", 2000);
|
||||||
|
long markReadWindow = Emulator.getConfig().getInt("mentions.markread.cooldown.ms", 500);
|
||||||
|
long markAllWindow = Emulator.getConfig().getInt("mentions.markall.cooldown.ms", 5000);
|
||||||
|
long deleteWindow = Emulator.getConfig().getInt("mentions.delete.cooldown.ms", 500);
|
||||||
|
|
||||||
|
prune(this.cooldowns, now, mentionWindow);
|
||||||
|
prune(this.roomBroadcastCooldowns, now, roomWindow);
|
||||||
|
prune(this.requestListCooldowns, now, requestWindow);
|
||||||
|
prune(this.markReadCooldowns, now, markReadWindow);
|
||||||
|
prune(this.markAllCooldowns, now, markAllWindow);
|
||||||
|
prune(this.deleteCooldowns, now, deleteWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void prune(ConcurrentHashMap<Integer, Long> bucket, long now, long windowMs) {
|
||||||
|
Iterator<Map.Entry<Integer, Long>> it = bucket.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Map.Entry<Integer, Long> entry = it.next();
|
||||||
|
Long value = entry.getValue();
|
||||||
|
if (value == null || (now - value) >= windowMs) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TRAILING_PUNCTUATION = ".,!?;:)]}\"'";
|
||||||
|
|
||||||
|
private static String trimTrailingPunctuation(String value) {
|
||||||
|
int end = value.length();
|
||||||
|
while (end > 0 && TRAILING_PUNCTUATION.indexOf(value.charAt(end - 1)) >= 0) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
return value.substring(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String truncate(String value, int max) {
|
||||||
|
if (value == null) return "";
|
||||||
|
if (value.length() <= max) return value;
|
||||||
|
return value.substring(0, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a present room occupant from a raw mention token. Tries the token
|
||||||
|
* verbatim first (so usernames containing allowed punctuation such as '-',
|
||||||
|
* '.', '!' still match), then falls back to a trailing-punctuation-trimmed
|
||||||
|
* form so a mention written as "@Bob!" still resolves the user "Bob".
|
||||||
|
*/
|
||||||
|
private Habbo resolveHabbo(Room room, String rawToken) {
|
||||||
|
Habbo habbo = room.getHabbo(rawToken);
|
||||||
|
if (habbo != null) {
|
||||||
|
return habbo;
|
||||||
|
}
|
||||||
|
String trimmed = trimTrailingPunctuation(rawToken);
|
||||||
|
if (!trimmed.isEmpty() && !trimmed.equals(rawToken)) {
|
||||||
|
return room.getHabbo(trimmed);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,6 @@ public class WordFilter {
|
|||||||
private static final Logger LOGGER = LoggerFactory.getLogger(WordFilter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(WordFilter.class);
|
||||||
|
|
||||||
private static final Pattern DIACRITICS_AND_FRIENDS = Pattern.compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");
|
private static final Pattern DIACRITICS_AND_FRIENDS = Pattern.compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");
|
||||||
//Configuration. Loaded from database & updated accordingly.
|
|
||||||
public static boolean ENABLED_FRIENDCHAT = true;
|
public static boolean ENABLED_FRIENDCHAT = true;
|
||||||
public static String DEFAULT_REPLACEMENT = "bobba";
|
public static String DEFAULT_REPLACEMENT = "bobba";
|
||||||
protected THashSet<WordFilterWord> autoReportWords = new THashSet<>();
|
protected THashSet<WordFilterWord> autoReportWords = new THashSet<>();
|
||||||
@@ -63,10 +62,12 @@ public class WordFilter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (word.autoReport)
|
if (!word.prefixOnly) {
|
||||||
this.autoReportWords.add(word);
|
if (word.autoReport)
|
||||||
else if (word.hideMessage)
|
this.autoReportWords.add(word);
|
||||||
this.hideMessageWords.add(word);
|
else if (word.hideMessage)
|
||||||
|
this.hideMessageWords.add(word);
|
||||||
|
}
|
||||||
|
|
||||||
this.words.add(word);
|
this.words.add(word);
|
||||||
}
|
}
|
||||||
@@ -146,6 +147,8 @@ public class WordFilter {
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
WordFilterWord word = (WordFilterWord) iterator.next();
|
WordFilterWord word = (WordFilterWord) iterator.next();
|
||||||
|
|
||||||
|
if (word.prefixOnly) continue;
|
||||||
|
|
||||||
if (StringUtils.containsIgnoreCase(filteredMessage, word.key)) {
|
if (StringUtils.containsIgnoreCase(filteredMessage, word.key)) {
|
||||||
if (habbo != null) {
|
if (habbo != null) {
|
||||||
if (Emulator.getPluginManager().fireEvent(new UserTriggerWordFilterEvent(habbo, word)).isCancelled())
|
if (Emulator.getPluginManager().fireEvent(new UserTriggerWordFilterEvent(habbo, word)).isCancelled())
|
||||||
@@ -179,6 +182,8 @@ public class WordFilter {
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
WordFilterWord word = (WordFilterWord) iterator.next();
|
WordFilterWord word = (WordFilterWord) iterator.next();
|
||||||
|
|
||||||
|
if (word.prefixOnly) continue;
|
||||||
|
|
||||||
if (StringUtils.containsIgnoreCase(message, word.key)) {
|
if (StringUtils.containsIgnoreCase(message, word.key)) {
|
||||||
if (habbo != null) {
|
if (habbo != null) {
|
||||||
if (Emulator.getPluginManager().fireEvent(new UserTriggerWordFilterEvent(habbo, word)).isCancelled())
|
if (Emulator.getPluginManager().fireEvent(new UserTriggerWordFilterEvent(habbo, word)).isCancelled())
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public class WordFilterWord {
|
|||||||
public final boolean hideMessage;
|
public final boolean hideMessage;
|
||||||
public final boolean autoReport;
|
public final boolean autoReport;
|
||||||
public final int muteTime;
|
public final int muteTime;
|
||||||
|
public final boolean prefixOnly;
|
||||||
|
|
||||||
public WordFilterWord(ResultSet set) throws SQLException {
|
public WordFilterWord(ResultSet set) throws SQLException {
|
||||||
this.key = set.getString("key");
|
this.key = set.getString("key");
|
||||||
@@ -16,13 +17,27 @@ public class WordFilterWord {
|
|||||||
this.hideMessage = set.getInt("hide") == 1;
|
this.hideMessage = set.getInt("hide") == 1;
|
||||||
this.autoReport = set.getInt("report") == 1;
|
this.autoReport = set.getInt("report") == 1;
|
||||||
this.muteTime = set.getInt("mute");
|
this.muteTime = set.getInt("mute");
|
||||||
|
this.prefixOnly = readBooleanColumn(set, "prefix_only");
|
||||||
}
|
}
|
||||||
|
|
||||||
public WordFilterWord(String key, String replacement) {
|
public WordFilterWord(String key, String replacement) {
|
||||||
|
this(key, replacement, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WordFilterWord(String key, String replacement, boolean prefixOnly) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.replacement = replacement;
|
this.replacement = replacement;
|
||||||
this.hideMessage = false;
|
this.hideMessage = false;
|
||||||
this.autoReport = false;
|
this.autoReport = false;
|
||||||
this.muteTime = 0;
|
this.muteTime = 0;
|
||||||
|
this.prefixOnly = prefixOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean readBooleanColumn(ResultSet set, String column) {
|
||||||
|
try {
|
||||||
|
return set.getInt(column) == 1;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import com.eu.habbo.messages.incoming.hotelview.*;
|
|||||||
import com.eu.habbo.messages.incoming.inventory.*;
|
import com.eu.habbo.messages.incoming.inventory.*;
|
||||||
import com.eu.habbo.messages.incoming.inventory.nickicons.*;
|
import com.eu.habbo.messages.incoming.inventory.nickicons.*;
|
||||||
import com.eu.habbo.messages.incoming.inventory.prefixes.*;
|
import com.eu.habbo.messages.incoming.inventory.prefixes.*;
|
||||||
|
import com.eu.habbo.messages.incoming.mentions.*;
|
||||||
import com.eu.habbo.messages.incoming.modtool.*;
|
import com.eu.habbo.messages.incoming.modtool.*;
|
||||||
import com.eu.habbo.messages.incoming.navigator.*;
|
import com.eu.habbo.messages.incoming.navigator.*;
|
||||||
import com.eu.habbo.messages.incoming.polls.AnswerPollEvent;
|
import com.eu.habbo.messages.incoming.polls.AnswerPollEvent;
|
||||||
@@ -426,6 +427,9 @@ public class PacketManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void registerRooms() throws Exception {
|
void registerRooms() throws Exception {
|
||||||
|
this.registerHandler(Incoming.RequestMentionsEvent, RequestMentionsEvent.class);
|
||||||
|
this.registerHandler(Incoming.MarkMentionsReadEvent, MarkMentionsReadEvent.class);
|
||||||
|
this.registerHandler(Incoming.DeleteMentionEvent, DeleteMentionEvent.class);
|
||||||
this.registerHandler(Incoming.RequestRoomLoadEvent, RequestRoomLoadEvent.class);
|
this.registerHandler(Incoming.RequestRoomLoadEvent, RequestRoomLoadEvent.class);
|
||||||
this.registerHandler(Incoming.RequestHeightmapEvent, RequestRoomHeightmapEvent.class);
|
this.registerHandler(Incoming.RequestHeightmapEvent, RequestRoomHeightmapEvent.class);
|
||||||
this.registerHandler(Incoming.RequestRoomHeightmapEvent, RequestRoomHeightmapEvent.class);
|
this.registerHandler(Incoming.RequestRoomHeightmapEvent, RequestRoomHeightmapEvent.class);
|
||||||
|
|||||||
@@ -496,4 +496,7 @@ public class Incoming {
|
|||||||
public static final int WheelAdminSavePrizesEvent = 9305;
|
public static final int WheelAdminSavePrizesEvent = 9305;
|
||||||
public static final int SoundboardPlayEvent = 9306;
|
public static final int SoundboardPlayEvent = 9306;
|
||||||
public static final int SoundboardSetEnabledEvent = 9307;
|
public static final int SoundboardSetEnabledEvent = 9307;
|
||||||
|
public static final int RequestMentionsEvent = 4803;
|
||||||
|
public static final int MarkMentionsReadEvent = 4804;
|
||||||
|
public static final int DeleteMentionEvent = 4805;
|
||||||
}
|
}
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.mentions;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.mentions.MentionManager;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
|
||||||
|
public class DeleteMentionEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
if (this.client == null || this.client.getHabbo() == null) return;
|
||||||
|
|
||||||
|
int userId = this.client.getHabbo().getHabboInfo().getId();
|
||||||
|
int mentionId = this.packet.readInt();
|
||||||
|
|
||||||
|
if (mentionId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionManager manager = Emulator.getGameEnvironment().getMentionManager();
|
||||||
|
|
||||||
|
if (!manager.tryAcquireDelete(userId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.delete(userId, mentionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.mentions;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.mentions.MentionManager;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
|
||||||
|
public class MarkMentionsReadEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
if (this.client == null || this.client.getHabbo() == null) return;
|
||||||
|
|
||||||
|
int userId = this.client.getHabbo().getHabboInfo().getId();
|
||||||
|
int mode = this.packet.readInt();
|
||||||
|
int mentionId = this.packet.readInt();
|
||||||
|
|
||||||
|
// Only mode 0 (mark-all) and mode 1 (mark-single) are valid. Reject
|
||||||
|
// anything else so a crafted packet can't fall into the mark-all branch
|
||||||
|
// by accident.
|
||||||
|
if (mode != 0 && mode != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 1 && mentionId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionManager manager = Emulator.getGameEnvironment().getMentionManager();
|
||||||
|
|
||||||
|
if (!manager.tryAcquireMarkRead(userId, mode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.markRead(userId, mode, mentionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.eu.habbo.messages.incoming.mentions;
|
||||||
|
|
||||||
|
import com.eu.habbo.Emulator;
|
||||||
|
import com.eu.habbo.habbohotel.mentions.HabboMention;
|
||||||
|
import com.eu.habbo.habbohotel.mentions.MentionManager;
|
||||||
|
import com.eu.habbo.messages.incoming.MessageHandler;
|
||||||
|
import com.eu.habbo.messages.outgoing.mentions.MentionsListComposer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RequestMentionsEvent extends MessageHandler {
|
||||||
|
@Override
|
||||||
|
public void handle() throws Exception {
|
||||||
|
if (this.client == null || this.client.getHabbo() == null) return;
|
||||||
|
|
||||||
|
int userId = this.client.getHabbo().getHabboInfo().getId();
|
||||||
|
|
||||||
|
MentionManager manager = Emulator.getGameEnvironment().getMentionManager();
|
||||||
|
|
||||||
|
if (!manager.tryAcquireRequestList(userId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = Emulator.getConfig().getInt("mentions.store.limit", 50);
|
||||||
|
|
||||||
|
List<HabboMention> mentions = manager.getMentions(userId, limit);
|
||||||
|
this.client.sendResponse(new MentionsListComposer(mentions));
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -34,6 +34,9 @@ public class RoomUserShoutEvent extends MessageHandler {
|
|||||||
if (RoomChatMessage.SAVE_ROOM_CHATS) {
|
if (RoomChatMessage.SAVE_ROOM_CHATS) {
|
||||||
Emulator.getThreading().run(message);
|
Emulator.getThreading().run(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Emulator.getGameEnvironment().getMentionManager()
|
||||||
|
.process(this.client.getHabbo(), this.client.getHabbo().getHabboInfo().getCurrentRoom(), message.getMessage(), RoomChatType.SHOUT);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String reportMessage = Emulator.getTexts().getValue("scripter.warning.chat.length").replace("%username%", this.client.getHabbo().getHabboInfo().getUsername()).replace("%length%", message.getMessage().length() + "");
|
String reportMessage = Emulator.getTexts().getValue("scripter.warning.chat.length").replace("%username%", this.client.getHabbo().getHabboInfo().getUsername()).replace("%length%", message.getMessage().length() + "");
|
||||||
|
|||||||
+3
@@ -36,6 +36,9 @@ public class RoomUserTalkEvent extends MessageHandler {
|
|||||||
if (RoomChatMessage.SAVE_ROOM_CHATS) {
|
if (RoomChatMessage.SAVE_ROOM_CHATS) {
|
||||||
Emulator.getThreading().run(message);
|
Emulator.getThreading().run(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Emulator.getGameEnvironment().getMentionManager()
|
||||||
|
.process(this.client.getHabbo(), room, message.getMessage(), RoomChatType.TALK);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String reportMessage = Emulator.getTexts().getValue("scripter.warning.chat.length").replace("%username%", this.client.getHabbo().getHabboInfo().getUsername()).replace("%length%", message.getMessage().length() + "");
|
String reportMessage = Emulator.getTexts().getValue("scripter.warning.chat.length").replace("%username%", this.client.getHabbo().getHabboInfo().getUsername()).replace("%length%", message.getMessage().length() + "");
|
||||||
|
|||||||
@@ -603,5 +603,7 @@ public class Outgoing {
|
|||||||
public static final int WheelAdminPrizesComposer = 9404;
|
public static final int WheelAdminPrizesComposer = 9404;
|
||||||
public static final int SoundboardSettingsComposer = 9405;
|
public static final int SoundboardSettingsComposer = 9405;
|
||||||
public static final int SoundboardPlayComposer = 9406;
|
public static final int SoundboardPlayComposer = 9406;
|
||||||
|
public static final int MentionReceivedComposer = 4801;
|
||||||
|
public static final int MentionsListComposer = 4802;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.mentions;
|
||||||
|
|
||||||
|
import com.eu.habbo.habbohotel.mentions.HabboMention;
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
public class MentionReceivedComposer extends MessageComposer {
|
||||||
|
private final HabboMention mention;
|
||||||
|
|
||||||
|
public MentionReceivedComposer(HabboMention mention) {
|
||||||
|
this.mention = mention;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.MentionReceivedComposer);
|
||||||
|
this.response.appendInt(this.mention.getId());
|
||||||
|
this.response.appendInt(this.mention.getSenderUserId());
|
||||||
|
this.response.appendString(this.mention.getSenderUsername());
|
||||||
|
this.response.appendInt(this.mention.getRoomId());
|
||||||
|
this.response.appendString(this.mention.getRoomName());
|
||||||
|
this.response.appendString(this.mention.getMessage());
|
||||||
|
this.response.appendInt(this.mention.getMentionType());
|
||||||
|
this.response.appendInt(this.mention.getTimestamp());
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package com.eu.habbo.messages.outgoing.mentions;
|
||||||
|
|
||||||
|
import com.eu.habbo.habbohotel.mentions.HabboMention;
|
||||||
|
import com.eu.habbo.messages.ServerMessage;
|
||||||
|
import com.eu.habbo.messages.outgoing.MessageComposer;
|
||||||
|
import com.eu.habbo.messages.outgoing.Outgoing;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MentionsListComposer extends MessageComposer {
|
||||||
|
private final List<HabboMention> mentions;
|
||||||
|
|
||||||
|
public MentionsListComposer(List<HabboMention> mentions) {
|
||||||
|
this.mentions = mentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServerMessage composeInternal() {
|
||||||
|
this.response.init(Outgoing.MentionsListComposer);
|
||||||
|
this.response.appendInt(this.mentions.size());
|
||||||
|
|
||||||
|
for (HabboMention mention : this.mentions) {
|
||||||
|
this.response.appendInt(mention.getId());
|
||||||
|
this.response.appendInt(mention.getSenderUserId());
|
||||||
|
this.response.appendString(mention.getSenderUsername());
|
||||||
|
this.response.appendInt(mention.getRoomId());
|
||||||
|
this.response.appendString(mention.getRoomName());
|
||||||
|
this.response.appendString(mention.getMessage());
|
||||||
|
this.response.appendInt(mention.getMentionType());
|
||||||
|
this.response.appendInt(mention.getTimestamp());
|
||||||
|
this.response.appendBoolean(mention.isRead());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user