对此有一个old question,但当时Smack不支持此XMPP扩展(XEP-0333)。在discourse.igniterealtime.org中也有一个question,但没有答案,它的重点是询问他们什么时候将为xep-0333扩展提供支持。
此刻Experimental Smack Extensions中的Smack支持它,您可以找到代码here。
到目前为止,我一直在寻找示例,指南或如何使用此扩展程序。 我也尝试在代码中进行挖掘,希望找到一些有关如何使用的javadoc,但也没有成功。
该问题的目标是获取一段代码片段,说明如何在smack 4.x.x中使用它
答案 0 :(得分:2)
更新:等待PR代码审查,以更新以下代码。
回答我自己的问题:
正如我所说的,Smack库具有对XEP-0333的实验性支持,在尝试将其与4.2和4.3.0-rc1版本一起使用后,我发现XEP-0333需要进行改进以使其像IncomingChatMessageListener
或{{ 1}}。
此外,我无法使其仅使用ChatStateListener
和OutgoingChatMessageListener
接口起作用,因为它们在内部检查IncomingChatMessageListener
是否带有<message>
标签(因为xmpp协议规则) ,但该规则不适用于XEP-0333:
当接收者发送聊天标记时,它应确保消息节仅包含“聊天标记”子元素,并可选地(适当时)包含线程子元素。自然,中间实体在路由或传递收据消息时可能会在消息中添加其他扩展元素,例如,延迟传递(XEP-0203)中指定的元素。
所以我所做的就是根据<body>
(第2版)和ChatMarkersManager
的体系结构和行为来改进ChatManager
类。
下面的代码是经过改进的代码(我仍然需要Smack库的反馈),但是它可以正常工作:
ChatStateManager
}
public final class ChatMarkersManager extends Manager {
private static final Map<XMPPConnection, ChatMarkersManager> INSTANCES = new WeakHashMap<>();
// @FORMATTER:OFF
private static final StanzaFilter FILTER = new NotFilter(new StanzaExtensionFilter(ChatMarkersElements.NAMESPACE));
private static final StanzaFilter CHAT_STATE_FILTER = new NotFilter(new StanzaExtensionFilter(ChatStateManager.NAMESPACE));
private static final StanzaFilter MESSAGE_FILTER = new OrFilter(
MessageTypeFilter.NORMAL_OR_CHAT,
MessageTypeFilter.GROUPCHAT
);
private static final StanzaFilter INCOMING_MESSAGE_FILTER = new AndFilter(
MESSAGE_FILTER,
new StanzaExtensionFilter(ChatMarkersElements.NAMESPACE)
);
// @FORMATTER:ON
private final Set<IncomingChatMarkerMessageListener> incomingListeners = new CopyOnWriteArraySet<>();
private final AsyncButOrdered<Chat> asyncButOrdered = new AsyncButOrdered<>();
private ChatMarkersManager(XMPPConnection connection) {
super(connection);
connection.addStanzaInterceptor(new StanzaListener() {
@Override
public void processStanza(Stanza packet)
throws
SmackException.NotConnectedException,
InterruptedException,
SmackException.NotLoggedInException {
Message message = (Message) packet;
if (shouldDiscardMessage(message)) {
return;
}
if (message.getBodies().isEmpty()) {
return;
}
// if message already has a chatMarkerExtension, then do nothing,
if (!FILTER.accept(message)) {
return;
}
// otherwise add a markable extension,
message.addExtension(new ChatMarkersElements.MarkableExtension());
}
}, MESSAGE_FILTER);
connection.addSyncStanzaListener(new StanzaListener() {
@Override
public void processStanza(Stanza packet)
throws
SmackException.NotConnectedException,
InterruptedException,
SmackException.NotLoggedInException {
final Message message = (Message) packet;
if (shouldDiscardMessage(message)) {
return;
}
EntityFullJid fullFrom = message.getFrom().asEntityFullJidIfPossible();
EntityBareJid bareFrom = fullFrom.asEntityBareJid();
final Chat chat = ChatManager.getInstanceFor(connection()).chatWith(bareFrom);
List<IncomingChatMarkerMessageListener> listeners;
synchronized (incomingListeners) {
listeners = new ArrayList<>(incomingListeners.size());
listeners.addAll(incomingListeners);
}
final List<IncomingChatMarkerMessageListener> finalListeners = listeners;
asyncButOrdered.performAsyncButOrdered(chat, new Runnable() {
@Override
public void run() {
for (IncomingChatMarkerMessageListener listener : finalListeners) {
if (ChatMarkersElements.MarkableExtension.from(message) != null) {
listener.newMarkableMessage(message);
} else if (ChatMarkersElements.ReceivedExtension.from(message) != null) {
listener.newReceivedMessage(message);
} else if (ChatMarkersElements.DisplayedExtension.from(message) != null) {
listener.newDisplayedMessage(message);
} else if (ChatMarkersElements.AcknowledgedExtension.from(message) != null) {
listener.newAcknowledgedMessage(message);
}
}
}
});
}
}, INCOMING_MESSAGE_FILTER);
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(ChatMarkersElements.NAMESPACE);
}
/**
* Get the singleton instance of ChatMarkersManager.
*
* @param connection
* @return the instance of ChatMarkersManager
*/
public static synchronized ChatMarkersManager getInstanceFor(XMPPConnection connection) {
ChatMarkersManager chatMarkersManager = INSTANCES.get(connection);
if (chatMarkersManager == null) {
chatMarkersManager = new ChatMarkersManager(connection);
INSTANCES.put(connection, chatMarkersManager);
}
return chatMarkersManager;
}
/**
* Register a IncomingChatMarkerMessageListener. That listener will be informed about new
* incoming markable messages.
*
* @param listener IncomingChatMarkerMessageListener
* @return true, if the listener was not registered before
*/
public boolean addIncomingChatMarkerMessageListener(IncomingChatMarkerMessageListener listener) {
synchronized (incomingListeners) {
return incomingListeners.add(listener);
}
}
/**
* Unregister a IncomingChatMarkerMessageListener.
*
* @param listener IncomingChatMarkerMessageListener
* @return true, if the listener was registered before
*/
public boolean removeIncomingChatMarkerMessageListener(IncomingChatMarkerMessageListener listener) {
synchronized (incomingListeners) {
return incomingListeners.remove(listener);
}
}
public void markMessageAsReceived(String id, Jid to, Jid from, String thread)
throws
SmackException.NotConnectedException,
InterruptedException {
Message message = createMessage(id, to, from, thread);
message.addExtension(new ChatMarkersElements.ReceivedExtension(id));
sendChatMarkerMessage(message);
}
public void markMessageAsDisplayed(String id, Jid to, Jid from, String thread)
throws
SmackException.NotConnectedException,
InterruptedException {
Message message = createMessage(id, to, from, thread);
message.addExtension(new ChatMarkersElements.DisplayedExtension(id));
sendChatMarkerMessage(message);
}
public void markMessageAsAcknowledged(String id, Jid to, Jid from, String thread)
throws
SmackException.NotConnectedException,
InterruptedException {
Message message = createMessage(id, to, from, thread);
message.addExtension(new ChatMarkersElements.AcknowledgedExtension(id));
sendChatMarkerMessage(message);
}
private Message createMessage(String id, Jid to, Jid from, String thread) {
Message message = new Message();
message.setStanzaId(id);
message.setTo(to);
message.setFrom(from);
message.setThread(thread);
return message;
}
private void sendChatMarkerMessage(Message message) throws SmackException.NotConnectedException, InterruptedException {
connection().sendStanza(message);
}
/**
* From XEP-0333, Protocol Format: The Chat Marker MUST have an 'id' which is the 'id' of the
* message being marked.
*
* @param message to be analyzed.
* @return true if the message contains a stanza Id.
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat Markers</a>
*/
private boolean shouldDiscardMessage(Message message) {
if (StringUtils.isNullOrEmpty(message.getStanzaId())) {
return true;
}
if (!CHAT_STATE_FILTER.accept(message)) {
ExtensionElement extension = message.getExtension(ChatStateManager.NAMESPACE);
String chatStateElementName = extension.getElementName();
ChatState state;
try {
state = ChatState.valueOf(chatStateElementName);
return !(state == ChatState.active);
} catch (Exception ex) {
return true;
}
}
return false;
}
/**
* Returns true if Chat Markers is supported by the server.
*
* @return true if Chat Markers is supported by the server.
* @throws SmackException.NotConnectedException
* @throws XMPPException.XMPPErrorException
* @throws SmackException.NoResponseException
* @throws InterruptedException
*/
public boolean isSupportedByServer()
throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException {
return ServiceDiscoveryManager.getInstanceFor(connection())
.serverSupportsFeature(ChatMarkersElements.NAMESPACE);
}
很简单:
interface
}
此刻,我不使用public interface IncomingChatMarkerMessageListener {
void newMarkableMessage(Message message);
void newReceivedMessage(Message message);
void newDisplayedMessage(Message message);
void newAcknowledgedMessage(Message message);
对象,因此没有通过接口传递它,但是可以轻松添加它。实际上,我希望能够处理Chat
和MultiChat。
此文件可以与8.5 Interaction with Chat States中的文档建议一起与XEP-0085一起使用。
就像我说的那样,要遵循协议规则,但仍然存在一些我无法回答的问题,我希望从Smack库伙计们那里得到一些反馈:D