-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathSimpleMessageRecipient.java
More file actions
249 lines (218 loc) · 10.2 KB
/
Copy pathSimpleMessageRecipient.java
File metadata and controls
249 lines (218 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package com.earth2me.essentials.messaging;
import com.earth2me.essentials.IEssentials;
import com.earth2me.essentials.IUser;
import com.earth2me.essentials.User;
import com.earth2me.essentials.adventure.AdventureUtil;
import net.ess3.api.events.PrivateMessagePreSendEvent;
import net.ess3.api.events.PrivateMessageSentEvent;
import org.bukkit.entity.Player;
import java.lang.ref.WeakReference;
import java.util.UUID;
import static com.earth2me.essentials.I18n.tlLiteral;
/**
* Represents a simple reusable implementation of {@link IMessageRecipient}. This class provides functionality for the following methods:
* <ul>
* <li>{@link IMessageRecipient#sendMessage(IMessageRecipient, String)}</li>
* <li>{@link IMessageRecipient#onReceiveMessage(IMessageRecipient, String)}</li>
* <li>{@link IMessageRecipient#getReplyRecipient()}</li>
* <li>{@link IMessageRecipient#setReplyRecipient(IMessageRecipient)}</li>
* </ul>
*
* <b>The given {@code parent} must implement the following methods to prevent overflow:</b>
* <ul>
* <li>{@link IMessageRecipient#sendMessage(String)}</li>
* <li>{@link IMessageRecipient#getName()}</li>
* <li>{@link IMessageRecipient#getDisplayName()}</li>
* <li>{@link IMessageRecipient#isReachable()}</li>
* </ul>
* <p>
* The reply-recipient is wrapped in a {@link WeakReference}.
*/
public class SimpleMessageRecipient implements IMessageRecipient {
private final IEssentials ess;
private final IMessageRecipient parent;
private long lastMessageMs;
private WeakReference<IMessageRecipient> replyRecipient;
public SimpleMessageRecipient(final IEssentials ess, final IMessageRecipient parent) {
this.ess = ess;
this.parent = parent;
}
protected static User getUser(final IMessageRecipient recipient) {
if (recipient instanceof SimpleMessageRecipient) {
return ((SimpleMessageRecipient) recipient).parent instanceof User ? (User) ((SimpleMessageRecipient) recipient).parent : null;
}
return recipient instanceof User ? (User) recipient : null;
}
@Override
public void sendMessage(final String message) {
this.parent.sendMessage(message);
}
@Override
public void sendTl(String tlKey, Object... args) {
this.parent.sendTl(tlKey, args);
}
@Override
public String tlSender(String tlKey, Object... args) {
return this.parent.tlSender(tlKey, args);
}
@Override
public String getName() {
return this.parent.getName();
}
@Override
public UUID getUUID() {
return this.parent.getUUID();
}
@Override
public String getDisplayName() {
return this.parent.getDisplayName();
}
@Override
public MessageResponse sendMessage(final IMessageRecipient recipient, String message) {
final PrivateMessagePreSendEvent preSendEvent = new PrivateMessagePreSendEvent(parent, recipient, message);
ess.getServer().getPluginManager().callEvent(preSendEvent);
if (preSendEvent.isCancelled()) {
return MessageResponse.EVENT_CANCELLED;
}
message = preSendEvent.getMessage();
final User senderUser = getUser(this);
// A muted player must not have their message delivered to the recipient. However, social spies
// may still observe the attempted message (see the socialspy-listen-muted-players setting).
// Note: PrivateMessageSentEvent is intentionally not fired here so the message is not relayed
// elsewhere (e.g. to Discord) as though it had actually been delivered.
if (senderUser != null && senderUser.isMuted()) {
sendSocialSpy(recipient, message, true);
senderUser.notifyMuted();
return MessageResponse.SENDER_MUTED;
}
final MessageResponse messageResponse = recipient.onReceiveMessage(this.parent, message);
switch (messageResponse) {
case UNREACHABLE:
sendTl("recentlyForeverAlone", recipient.getDisplayName());
break;
case MESSAGES_IGNORED:
sendTl("msgIgnore", recipient.getDisplayName());
break;
case SENDER_IGNORED:
break;
// When this recipient is AFK, notify the sender. Then, proceed to send the message.
case SUCCESS_BUT_AFK:
// Currently, only IUser can be afk, so we unsafely cast to get the afk message.
if (((IUser) recipient).getAfkMessage() != null) {
sendTl("userAFKWithMessage", recipient.getDisplayName(), ((IUser) recipient).getAfkMessage());
} else {
sendTl("userAFK", recipient.getDisplayName());
}
// fall through
default:
sendTl("msgFormat", AdventureUtil.parsed(tlSender("meSender")), recipient.getDisplayName(), message);
// Better Social Spy
sendSocialSpy(recipient, message, false);
break;
}
// If the message was a success, set this sender's reply-recipient to the current recipient.
if (messageResponse.isSuccess()) {
setReplyRecipient(recipient);
}
final PrivateMessageSentEvent sentEvent = new PrivateMessageSentEvent(parent, recipient, message, messageResponse);
ess.getServer().getPluginManager().callEvent(sentEvent);
return messageResponse;
}
/**
* Shows a private message to all online social spies, unless either party is exempt from being spied on.
*
* @param recipient the recipient of the private message
* @param message the message that was sent
* @param muted whether the sender is muted; muted messages are only shown when
* {@code socialspy-listen-muted-players} is enabled and use a distinct prefix
*/
private void sendSocialSpy(final IMessageRecipient recipient, final String message, final boolean muted) {
if (!ess.getSettings().isSocialSpyMessages()) {
return;
}
if (muted && !ess.getSettings().getSocialSpyListenMutedPlayers()) {
return;
}
final User senderUser = getUser(this);
final User recipientUser = getUser(recipient);
// Don't spy on chats involving socialspy exempt players.
if (senderUser == null // not null if player.
|| senderUser.isAuthorized("essentials.chat.spy.exempt")
|| recipientUser == null
|| recipientUser.isAuthorized("essentials.chat.spy.exempt")) {
return;
}
final String senderName = ess.getSettings().isSocialSpyDisplayNames() ? getDisplayName() : getName();
final String recipientName = ess.getSettings().isSocialSpyDisplayNames() ? recipient.getDisplayName() : recipient.getName();
final String prefix = muted ? tlSender("socialSpyMutedPrefix") : tlLiteral("socialSpyPrefix");
for (final User onlineUser : ess.getOnlineUsers()) {
if (onlineUser.isSocialSpyEnabled()
// Don't send socialspy messages to message sender/receiver to prevent spam
&& !onlineUser.equals(senderUser)
&& !onlineUser.equals(recipient)) {
onlineUser.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(prefix + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message)));
}
}
}
@Override
public MessageResponse onReceiveMessage(final IMessageRecipient sender, final String message) {
if (!isReachable()) {
return MessageResponse.UNREACHABLE;
}
final User user = getUser(this);
boolean afk = false;
boolean isLastMessageReplyRecipient = ess.getSettings().isLastMessageReplyRecipient();
if (user != null) {
if (user.isIgnoreMsg() && sender instanceof IUser && !((IUser) sender).isAuthorized("essentials.msgtoggle.bypass")) { // Don't ignore console and senders with permission
return MessageResponse.MESSAGES_IGNORED;
}
afk = user.isAfk();
isLastMessageReplyRecipient = user.isLastMessageReplyRecipient();
// Check whether this recipient ignores the sender, only if the sender is not the console.
if (sender instanceof IUser && user.isIgnoredPlayer((IUser) sender)) {
return MessageResponse.SENDER_IGNORED;
}
}
// Display the formatted message to this recipient.
sendTl("msgFormat", sender.getDisplayName(), AdventureUtil.parsed(tlSender("meRecipient")), message);
if (isLastMessageReplyRecipient) {
// If this recipient doesn't have a reply recipient, initiate by setting the first
// message sender to this recipient's replyRecipient.
final long timeout = ess.getSettings().getLastMessageReplyRecipientTimeout() * 1000;
if (getReplyRecipient() == null || !getReplyRecipient().isReachable()
|| System.currentTimeMillis() - this.lastMessageMs > timeout) {
setReplyRecipient(sender);
}
} else { // Old message functionality, always set the reply recipient to the last person who sent us a message.
setReplyRecipient(sender);
}
this.lastMessageMs = System.currentTimeMillis();
return afk ? MessageResponse.SUCCESS_BUT_AFK : MessageResponse.SUCCESS;
}
@Override
public boolean isReachable() {
return this.parent.isReachable();
}
/**
* {@inheritDoc}
* <p>
* <b>This {@link com.earth2me.essentials.messaging.SimpleMessageRecipient} implementation stores the a weak reference to the recipient.</b>
*/
@Override
public IMessageRecipient getReplyRecipient() {
return replyRecipient == null ? null : replyRecipient.get();
}
/**
* {@inheritDoc}
* <p>
* <b>This {@link com.earth2me.essentials.messaging.SimpleMessageRecipient} implementation stores the a weak reference to the recipient.</b>
*/
@Override
public void setReplyRecipient(final IMessageRecipient replyRecipient) {
this.replyRecipient = new WeakReference<>(replyRecipient);
}
@Override
public boolean isHiddenFrom(Player player) {
return parent.isHiddenFrom(player);
}
}