me.ixfan.wechatkit.message.MessageManager.java Source code

Java tutorial

Introduction

Here is the source code for me.ixfan.wechatkit.message.MessageManager.java

Source

/*
 * MIT License
 *
 * Copyright (c) 2016 Warren Fan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package me.ixfan.wechatkit.message;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import me.ixfan.wechatkit.WeChatKitComponent;
import me.ixfan.wechatkit.common.WeChatApiResult;
import me.ixfan.wechatkit.common.WeChatConstants;
import me.ixfan.wechatkit.exceptions.WeChatApiErrorException;
import me.ixfan.wechatkit.exceptions.WechatXmlMessageParseException;
import me.ixfan.wechatkit.exceptions.WechatXmlMessageSerializationException;
import me.ixfan.wechatkit.material.MediaObject;
import me.ixfan.wechatkit.message.in.*;
import me.ixfan.wechatkit.message.in.event.*;
import me.ixfan.wechatkit.message.out.OutMessageType;
import me.ixfan.wechatkit.message.out.json.MessageForMassSend;
import me.ixfan.wechatkit.message.out.template.MessageTemplate;
import me.ixfan.wechatkit.message.out.template.TemplateMessageForSend;
import me.ixfan.wechatkit.message.out.xml.*;
import me.ixfan.wechatkit.token.TokenManager;
import me.ixfan.wechatkit.util.HttpClientUtil;
import me.ixfan.wechatkit.util.JAXBUtil;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.http.util.Args;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.util.*;

/**
 * ??
 *
 * Created by Warran Fan on 16/3/26.
 */
public class MessageManager extends WeChatKitComponent {

    public MessageManager(String wechatAccountId, TokenManager tokenManager) {
        super(wechatAccountId, tokenManager);
    }

    /**
     * ?? XML ??,?
     *
     * @param msgInXml ? XML ??
     * @return ?
     */
    public ReceivedMsg parseXmlMessage(final String msgInXml) throws WechatXmlMessageParseException {

        DocumentBuilder documentBuilder;
        try {
            documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new WechatXmlMessageParseException("Failed to create DocumentBuilder instance.", e);
        }

        Document document;
        try {
            document = documentBuilder.parse(new InputSource(new StringReader(msgInXml)));
        } catch (Exception e) {
            throw new WechatXmlMessageParseException("Failed to parse XML message.", e);
        }

        NodeList msgTypeNodes = document.getElementsByTagName("MsgType");
        if (null == msgTypeNodes || msgTypeNodes.getLength() == 0) {
            throw new WechatXmlMessageParseException("Invalid XML message!");
        }

        InMessageType msgType = InMessageType.of(msgTypeNodes.item(0).getFirstChild().getNodeValue());
        switch (msgType) {
        case TEXT:
            try {
                return JAXBUtil.unmarshal(msgInXml, ReceivedTextMsg.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException("Failed to deserialize text message in XML to object.", e);
            }
        case IMAGE:
            try {
                return JAXBUtil.unmarshal(msgInXml, ReceivedImageMsg.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException("Failed to deserialize image message in XML to object.",
                        e);
            }
        case VOICE:
            try {
                return JAXBUtil.unmarshal(msgInXml, ReceivedVoiceMsg.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException("Failed to deserialize voice message in XML to object.",
                        e);
            }
        case VIDEO:
        case SHORT_VIDEO:
            try {
                return JAXBUtil.unmarshal(msgInXml, ReceivedVideoMsg.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException("Failed to deserialize video message in XML to object.",
                        e);
            }
        case LOCATION:
            try {
                return JAXBUtil.unmarshal(msgInXml, ReceivedLocationMsg.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException("Failed to deserialize location message in XML to object.",
                        e);
            }
        case LINK:
            try {
                return JAXBUtil.unmarshal(msgInXml, ReceivedLinkMsg.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException("Failed to deserialize link message in XML to object.", e);
            }
        case EVENT:
            EventType eventType = EventType
                    .of(document.getElementsByTagName("Event").item(0).getFirstChild().getNodeValue());
            return parseEventMessage(msgInXml, eventType);
        default:
            return null;
        }

    }

    /**
     * ???? XML ?
     *
     * @param responseMessage ??
     * @return ?? XML ?
     */
    public String generateResponseMessageInXml(ResponseMsg responseMessage)
            throws WechatXmlMessageSerializationException {
        if (responseMessage instanceof ResponseTextMsg) {
            try {
                return JAXBUtil.marshal((ResponseTextMsg) responseMessage);
            } catch (Exception e) {
                throw new WechatXmlMessageSerializationException("?XML?", e);
            }
        } else if (responseMessage instanceof ResponseImageMsg) {
            try {
                return JAXBUtil.marshal((ResponseImageMsg) responseMessage);
            } catch (Exception e) {
                throw new WechatXmlMessageSerializationException("?XML?", e);
            }
        } else if (responseMessage instanceof ResponseVoiceMsg) {
            try {
                return JAXBUtil.marshal((ResponseVoiceMsg) responseMessage);
            } catch (Exception e) {
                throw new WechatXmlMessageSerializationException("?XML?", e);
            }
        } else if (responseMessage instanceof ResponseMusicMsg) {
            try {
                return JAXBUtil.marshal((ResponseMusicMsg) responseMessage);
            } catch (Exception e) {
                throw new WechatXmlMessageSerializationException("?XML?", e);
            }
        } else if (responseMessage instanceof ResponseVideoMsg) {
            try {
                return JAXBUtil.marshal((ResponseVideoMsg) responseMessage);
            } catch (Exception e) {
                throw new WechatXmlMessageSerializationException("?XML?", e);
            }
        } else if (responseMessage instanceof ResponseNewsMsg) {
            try {
                return JAXBUtil.marshal((ResponseNewsMsg) responseMessage);
            } catch (Exception e) {
                throw new WechatXmlMessageSerializationException("?XML?", e);
            }
        }
        return "success";
    }

    /**
     * ?? XML ?
     *
     * @param toUserOpenid ? openid
     * @param content ?
     * @return ?? XML ?
     */
    public String generateTextResponseMessageInXml(String toUserOpenid, String content) {
        ResponseTextMsg textMsg = new ResponseTextMsg(super.wechatId, toUserOpenid, System.currentTimeMillis(),
                content);
        try {
            return JAXBUtil.marshal(textMsg);
        } catch (Exception e) {
            throw new WechatXmlMessageSerializationException("?XML?", e);
        }
    }

    /**
     * ?? XML ?
     *
     * @param toUserOpenid ?openid
     * @param mediaId ????ID
     * @return ?? XML ?
     */
    public String generateImageResponseMessageInXml(String toUserOpenid, String mediaId) {
        ResponseImageMsg imageMsg = new ResponseImageMsg(super.wechatId, toUserOpenid, System.currentTimeMillis());
        imageMsg.setMediaImage(new MediaObject(mediaId));
        try {
            return JAXBUtil.marshal(imageMsg);
        } catch (Exception e) {
            throw new WechatXmlMessageSerializationException("?XML?", e);
        }
    }

    /**
     * ?? XML ?
     *
     * @param toUserOpenid ?openid
     * @param mediaId ????ID
     * @return ?? XML ?
     */
    public String generateVoiceResponseMessageInXml(String toUserOpenid, String mediaId) {
        ResponseVoiceMsg voiceMsg = new ResponseVoiceMsg(super.wechatId, toUserOpenid, System.currentTimeMillis());
        voiceMsg.setMediaVoice(new MediaObject(mediaId));
        try {
            return JAXBUtil.marshal(voiceMsg);
        } catch (Exception e) {
            throw new WechatXmlMessageSerializationException("?XML?", e);
        }
    }

    /**
     * ?? XML ?
     * @param toUserOpenid ? openid
     * @param title ?
     * @param description ???
     * @param mediaId ????ID
     * @return ?? XML ?
     */
    public String generateVideoResponseMessageInXml(String toUserOpenid, String title, String description,
            String mediaId) {
        ResponseVideoMsg videoMsg = new ResponseVideoMsg(super.wechatId, toUserOpenid, System.currentTimeMillis());
        videoMsg.setVideo(new Video(title, description, mediaId));
        try {
            return JAXBUtil.marshal(videoMsg);
        } catch (Exception e) {
            throw new WechatXmlMessageSerializationException("?XML?", e);
        }
    }

    /**
     * ??? XML ?
     * @param toUserOpenid ? openid
     * @param music ?????
     * @return ??? XML ?
     */
    public String generateMusicResponseMessageInXml(String toUserOpenid, Music music) {
        ResponseMusicMsg musicMsg = new ResponseMusicMsg(super.wechatId, toUserOpenid, System.currentTimeMillis());
        musicMsg.setMusic(music);
        try {
            return JAXBUtil.marshal(musicMsg);
        } catch (Exception e) {
            throw new WechatXmlMessageSerializationException("?XML?", e);
        }
    }

    /**
     * ?? XML ?
     * @param toUserOpenid ? openid
     * @param articles ???item?10??
     * @return ?? XML ?
     */
    public String generateNewsResponseMessageInXml(String toUserOpenid, Article... articles) {
        ResponseNewsMsg newsMsg = new ResponseNewsMsg(super.wechatId, toUserOpenid, System.currentTimeMillis());
        newsMsg.setArticles(Arrays.asList(articles));
        try {
            return JAXBUtil.marshal(newsMsg);
        } catch (Exception e) {
            throw new WechatXmlMessageSerializationException("?XML?", e);
        }
    }

    /**
     * ? OpenId ??
     * @param content ?
     * @param openIds OpenId 
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassTextMessageToUsers(String content, String... openIds) {
        Args.notEmpty(content, "?");
        Args.notNull(openIds, "OpenId");
        Args.notEmpty(Arrays.asList(openIds), "OpenId");

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SNED_BY_OPENIDS.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.TEXT, content, Arrays.asList(openIds));
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * @param content ?
     * @param tagId ?tag_id???is_to_alltrue??tag_id
     * @param isToAll ????truefalsetrue??false??tag_id??
     *                ??????
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassTextMessageToUsersWithTag(String content, String tagId, boolean isToAll) {
        Args.notEmpty(content, "?");
        if (!isToAll) {
            Args.notEmpty(tagId, "ID");
        }

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SEND_BY_TAG.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.TEXT, content, tagId, isToAll);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ? OpenId ??
     * @param mediaId ? media_id
     * @param openIds OpenId 
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassImageToUsers(String mediaId, String... openIds) {
        Args.notEmpty(mediaId, "Media ID of image material");
        Args.notNull(openIds, "OpenId list");
        Args.notEmpty(Arrays.asList(openIds), "OpenId list");

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SNED_BY_OPENIDS.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.IMAGE, mediaId, Arrays.asList(openIds));
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * @param mediaId ? media_id
     * @param tagId ?tag_id???is_to_alltrue??tag_id
     * @param isToAll ????truefalsetrue??false??tag_id??
     *                ??????
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassImageToUsersWithTag(String mediaId, String tagId, boolean isToAll) {
        Args.notEmpty(mediaId, "Media ID of image material");
        if (!isToAll) {
            Args.notEmpty(tagId, "Tag ID");
        }

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SEND_BY_TAG.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.IMAGE, mediaId, tagId, false);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ? OpenId ??
     * @param mediaId ? media_id
     * @param openIds OpenId 
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassVoiceMessageToUsers(String mediaId, String... openIds) {
        Args.notEmpty(mediaId, "Media ID of voice material");
        Args.notNull(openIds, "OpenId list");
        Args.notEmpty(Arrays.asList(openIds), "OpenId list");

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SNED_BY_OPENIDS.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.VOICE, mediaId, Arrays.asList(openIds));
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * @param mediaId ? media_id
     * @param tagId ?tag_id???is_to_alltrue??tag_id
     * @param isToAll ????truefalsetrue??false??tag_id??
     *                ??????
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassVoiceMessageToUsersWithTag(String mediaId, String tagId, boolean isToAll) {
        Args.notEmpty(mediaId, "Media ID of voice material");
        if (!isToAll) {
            Args.notEmpty(tagId, "Tag ID");
        }

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SEND_BY_TAG.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.VOICE, mediaId, tagId, false);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ? OpenId ??
     * @param mediaId ??? media_id
     * @param openIds OpenId 
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassVideoMessageToUsers(String mediaId, String... openIds) {
        Args.notEmpty(mediaId, "Media ID of video material");
        Args.notNull(openIds, "OpenId list");
        Args.notEmpty(Arrays.asList(openIds), "OpenId list");

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SNED_BY_OPENIDS.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.MP_VIDEO, mediaId, Arrays.asList(openIds));
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * @param mediaId ??? media_id
     * @param tagId ?tag_id???is_to_alltrue??tag_id
     * @param isToAll ????truefalsetrue??false??tag_id??
     *                ??????
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassVideoMessageToUsersWithTag(String mediaId, String tagId, boolean isToAll) {
        Args.notEmpty(mediaId, "Media ID of video material");
        if (!isToAll) {
            Args.notEmpty(tagId, "Tag ID");
        }

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SEND_BY_TAG.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.MP_VIDEO, mediaId, tagId, false);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ? OpenId ???
     * @param mediaId ???? media_id
     * @param openIds OpenId 
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassMusicToUsers(String mediaId, String... openIds) {
        Args.notEmpty(mediaId, "Media ID of music material");
        Args.notNull(openIds, "OpenId list");
        Args.notEmpty(Arrays.asList(openIds), "OpenId list");

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SNED_BY_OPENIDS.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.MUSIC, mediaId, Arrays.asList(openIds));
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ????
     * @param mediaId ???? media_id
     * @param tagId ?tag_id???is_to_alltrue??tag_id
     * @param isToAll ????truefalsetrue??false??tag_id??
     *                ??????
     * @return ???? {@link WeChatApiResult#getMsgId()} ????ID
     * ??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??
     */
    public WeChatApiResult sendMassMusicToUsersWithTag(String mediaId, String tagId, boolean isToAll) {
        Args.notEmpty(mediaId, "Media ID of music material");
        if (!isToAll) {
            Args.notEmpty(tagId, "Tag ID");
        }

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SEND_BY_TAG.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.MUSIC, mediaId, tagId, false);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ? OpenId ??
     * @param mediaId ??? media_id
     * @param ignoreReprint <p>??? false</p>
     *                      <p>true  - ??</p>
     *                      <p>false - ???</p>
     * @param openIds OpenId 
     * @return <p>???? {@link WeChatApiResult#getMsgId()} ????ID
     *  {@link WeChatApiResult#getMsgDataId()} ???ID??????????????msgid??????msgid?</p>
     * <p>??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??</p>
     */
    public WeChatApiResult sendMassArticleToUsers(String mediaId, boolean ignoreReprint, String... openIds) {
        Args.notEmpty(mediaId, "Media ID of news material");
        Args.notNull(openIds, "OpenId list");
        Args.notEmpty(Arrays.asList(openIds), "OpenId list");

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SNED_BY_OPENIDS.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.MP_NEWS, mediaId, Arrays.asList(openIds));
        msg.setSendIgnoreReprint(ignoreReprint ? 1 : 0);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ???
     * @param mediaId ??? media_id
     * @param tagId ?tag_id???is_to_alltrue??tag_id
     * @param isToAll ????truefalsetrue??false??tag_id??
     *                ??????
     * @param ignoreReprint <p>??? false</p>
     *                      <p>true  - ??</p>
     *                      <p>false - ???</p>
     * @return <p>???? {@link WeChatApiResult#getMsgId()} ????ID
     *  {@link WeChatApiResult#getMsgDataId()} ???ID??????????????msgid??????msgid?</p>
     * <p>??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??</p>
     */
    public WeChatApiResult sendMassArticleToUsersWithTag(String mediaId, String tagId, boolean isToAll,
            boolean ignoreReprint) {
        Args.notEmpty(mediaId, "Media ID of news material");
        if (!isToAll) {
            Args.notEmpty(tagId, "Tag ID");
        }

        final String url = WeChatConstants.WECHAT_POST_MESSAGE_MASS_SEND_BY_TAG.replace("${ACCESS_TOKEN}",
                super.getTokenManager().getAccessToken());
        MessageForMassSend msg = new MessageForMassSend(OutMessageType.MP_NEWS, mediaId, tagId, false);
        msg.setSendIgnoreReprint(ignoreReprint ? 1 : 0);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url, msg.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ?????
     *
     * @param templateId ??ID
     * @param openid ? openid
     * @param contentParams <p>????key-value pairs? key ???????
     *                       key ????</p>
     *                      <p>???? {{withdrawMoney.DATA}}? key  "withdrawMoney"
     *                      ?? #173177 key  "withdrawMoney#173177"</p>
     * @return <p>???????? {@link WeChatApiResult#getMsgId()} ???ID
     * ??????????????????</p>
     * <p>??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??</p>
     */
    public WeChatApiResult sendTemplateMessage(String templateId, String openid,
            Map<String, String> contentParams) {
        Args.notEmpty(templateId, "ID of message template");
        Args.notEmpty(openid, "OpenID of message receiver");
        final String url = WeChatConstants.WECHAT_POST_SEND_TEMPLATE_MESSAGE.replace("${ACCESS_TOKEN}",
                super.tokenManager.getAccessToken());
        final TemplateMessageForSend templateMessageForSend = new TemplateMessageForSend(templateId, openid,
                contentParams);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(url,
                    templateMessageForSend.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ?????
     * @param templateId ??ID
     * @param openid ? openid
     * @param url ?
     * @param contentParams <p>????key-value pairs? key ???????
     *                       key ????</p>
     *                      <p>???? {{withdrawMoney.DATA}}? key  "withdrawMoney"
     *                      ?? #173177 key  "withdrawMoney#173177"</p>
     * @return <p>???????? {@link WeChatApiResult#getMsgId()} ???ID
     * ??????????????????</p>
     * <p>??? {@link WeChatApiResult#getErrcode()}  {@link WeChatApiResult#getErrmsg()}
     * ??</p>
     */
    public WeChatApiResult sendTemplateMessage(String templateId, String openid, String url,
            Map<String, String> contentParams) {
        Args.notEmpty(templateId, "ID of message template");
        Args.notEmpty(openid, "OpenID of message receiver");
        final String postUrl = WeChatConstants.WECHAT_POST_SEND_TEMPLATE_MESSAGE.replace("${ACCESS_TOKEN}",
                super.tokenManager.getAccessToken());
        final TemplateMessageForSend templateMessageForSend = new TemplateMessageForSend(templateId, openid, url,
                contentParams);
        try {
            JsonObject jsonResponse = HttpClientUtil.sendPostRequestWithJsonBody(postUrl,
                    templateMessageForSend.toJsonString());
            return WeChatApiResult.instanceOf(jsonResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ?????
     * @return ????
     * @throws WeChatApiErrorException API??
     */
    public List<MessageTemplate> retrieveMessageTemplates() throws WeChatApiErrorException {
        final String url = WeChatConstants.WECHAT_GET_MESSAGE_TEMPLATES.replace("${ACCESS_TOKEN}",
                super.tokenManager.getAccessToken());
        JsonObject jsonResponse;
        try {
            jsonResponse = HttpClientUtil.sendGetRequestAndGetJsonResponse(url);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if (!jsonResponse.has("template_list")) {
            throw new WeChatApiErrorException(jsonResponse.get("errcode").getAsInt(),
                    jsonResponse.get("errmsg").getAsString());
        } else {
            JsonArray teplArray = jsonResponse.getAsJsonArray("template_list");
            if (teplArray.size() == 0) {
                return Collections.emptyList();
            }
            Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                    .create();
            return gson.fromJson(teplArray, new TypeToken<ArrayList<MessageTemplate>>() {
            }.getType());
        }

    }

    /**
     * ??
     *
     * @param msgInXml XML ??
     * @param eventType 
     * @return ???
     */
    private ReceivedMsg parseEventMessage(final String msgInXml, EventType eventType)
            throws WechatXmlMessageParseException {
        switch (eventType) {
        case SUBSCRIBE:
            try {
                return JAXBUtil.unmarshal(msgInXml, SubscribeEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize subscribe event message in XML to object.", e);
            }
        case UNSUBSCRIBE:
            try {
                return JAXBUtil.unmarshal(msgInXml, UnsubscribeEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize unsubscribe event message in XML to object.", e);
            }
        case SCAN:
            try {
                return JAXBUtil.unmarshal(msgInXml, QrSceneEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize QR scan event message in XML to object.", e);
            }
        case LOCATION:
            try {
                return JAXBUtil.unmarshal(msgInXml, LocationEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize location event message in XML to object.", e);
            }
        case MENU_CLICK:
            try {
                return JAXBUtil.unmarshal(msgInXml, MenuClickEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize menu CLICK event message in XML to object.", e);
            }
        case MENU_VIEW:
            try {
                return JAXBUtil.unmarshal(msgInXml, MenuViewEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize menu VIEW event message in XML to object.", e);
            }
        case MENU_PIC_OR_ALBUM:
        case MENU_PIC_SYS_PHOTO:
        case MENU_PIC_WEIXIN:
            try {
                return JAXBUtil.unmarshal(msgInXml, MenuSendPictureEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize menu send picture event message in XML to object.", e);
            }
        case MENU_SCAN_CODE_PUSH:
        case MENU_SCAN_CODE_WAIT_MSG:
            try {
                return JAXBUtil.unmarshal(msgInXml, MenuScanCodeEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize menu scan code event message in XML to object.", e);
            }
        case MENU_LOCATION_SELECT:
            try {
                return JAXBUtil.unmarshal(msgInXml, MenuSendLocationEvent.class);
            } catch (JAXBException e) {
                throw new WechatXmlMessageParseException(
                        "Failed to deserialize menu send location event message in XML to object.", e);
            }
        default:
            return null;
        }
    }

}