【问题标题】:Chat Markers (XEP-0333) in SmackSmack 中的聊天标记 (XEP-0333)
【发布时间】:2018-07-17 20:43:11
【问题描述】:

有一个 old question 与此有关,但当时 Smack 不支持此 XMPP 扩展 (XEP-0333)。在 discourse.igniterealtime.org 中也有一个 question,但它没有答案,它的重点是询问他们何时会支持 xep-0333 扩展。

目前 Smack 在Experimental Smack Extensions 支持它,您可以找到代码here

到目前为止,我一直在寻找示例、指南或如何使用此扩展程序,但没有成功。 我也尝试挖掘代码,希望能找到一些关于如何使用的 javadoc,但也没有成功。

这个问题的目标是获取如何在 smack 4.x.x 中使用它的 sn-p 代码

【问题讨论】:

    标签: android xmpp smack


    【解决方案1】:

    更新:等待 PR 代码审查以更新以下代码。

    回答我自己的问题:
    正如我所说,Smack 库对 XEP-0333 具有实验性支持,在尝试将其与版本 4.2 和 4.3.0-rc1 一起使用后,我发现需要改进 XEP-0333 以使其像 IncomingChatMessageListenerChatStateListener 一样工作。

    此外,我无法使其仅使用 OutgoingChatMessageListenerIncomingChatMessageListener 接口工作,因为它们在内部检查 <message> 是否有 <body> 标记(因为 xmpp 协议规则),但该规则不适用XEP-0333:

    当接收者发送一个聊天标记时,它应该确保消息节只包含聊天标记子元素和可选的(在适当的时候)一个线程子元素。自然,中间实体可能会在路由或传递接收消息时向消息添加其他扩展元素,例如延迟传递 (XEP-0203) 中指定的元素。

    所以我所做的是按照ChatManager(版本2)和ChatStateManager的架构和行为改进ChatMarkersManager类。

    以下代码是改进后的代码(我仍然需要 Smack 库人员的反馈),但它可以正常工作:

    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 对象,所以我没有通过接口传递它,但可以轻松添加它。其实我想能够处理Chat和MultiChat。

    这可以与 XEP-0085 一起使用,正如 8.5 Interaction with Chat States 中的文档所建议的那样。

    就像我在它工作之前所说的那样,遵循协议规则,但我有一些我无法回答的问题,我希望从 Smack 库人员那里得到一些反馈:D

    【讨论】:

    • 嗨,你在使用这个 ChatMarkersManager。其实我对使用这个实现很感兴趣,想知道你对此的反馈。适合你吗?
    • 嗨@umerk44!是的,该应用程序使用的版本与此版本非常相似,但有一些改进。您可以在 GitHub 中查看 PR。
    猜你喜欢
    • 2015-12-03
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 2015-05-30
    • 2011-12-05
    • 2020-12-10
    • 2018-03-21
    • 2018-12-12
    相关资源
    最近更新 更多