All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.firefly.wechat.service.impl.WechatMessageServiceImpl Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
package com.firefly.wechat.service.impl;

import com.firefly.server.http2.router.RoutingContext;
import com.firefly.utils.Assert;
import com.firefly.utils.codec.HexUtils;
import com.firefly.utils.function.Action1;
import com.firefly.utils.function.Action2;
import com.firefly.utils.function.Action3;
import com.firefly.wechat.model.CommonRequest;
import com.firefly.wechat.model.EchoRequest;
import com.firefly.wechat.model.message.*;
import com.firefly.wechat.service.WechatMessageService;
import com.firefly.wechat.utils.CtxUtils;
import com.firefly.wechat.utils.MessageXmlUtils;
import com.firefly.wechat.utils.WXBizMsgCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

/**
 * @author Pengtao Qiu
 */
public class WechatMessageServiceImpl implements WechatMessageService {

    private static Logger log = LoggerFactory.getLogger("firefly-system");

    private String wechatToken;
    private String aesKey;
    private String appId;

    private List> textMessageListeners = new LinkedList<>();
    private List> imageMessageListeners = new LinkedList<>();
    private List> voiceMessageListeners = new LinkedList<>();
    private List> videoMessageListeners = new LinkedList<>();
    private List> locationMessageListeners = new LinkedList<>();
    private List> linkMessageListeners = new LinkedList<>();
    private List> subscribedMessageListeners = new LinkedList<>();
    private List> unsubscribedMessageListeners = new LinkedList<>();
    private List> scanMessageListeners = new LinkedList<>();
    private List> clickMessageListeners = new LinkedList<>();
    private List> viewMessageListeners = new LinkedList<>();
    private List> reportMessageListeners = new LinkedList<>();
    private List> echoListeners = new LinkedList<>();
    private List> otherRequestListeners = new LinkedList<>();

    public String getWechatToken() {
        return wechatToken;
    }

    public void setWechatToken(String wechatToken) {
        this.wechatToken = wechatToken;
    }

    public String getAesKey() {
        return aesKey;
    }

    public void setAesKey(String aesKey) {
        this.aesKey = aesKey;
    }

    public String getAppId() {
        return appId;
    }

    @Override
    public void onRequest(RoutingContext ctx) {
        CommonRequest commonRequest = CtxUtils.toRequest(ctx);
        if (commonRequest instanceof EchoRequest) {
            if (echoListeners.isEmpty()) {
                EchoRequest req = (EchoRequest) commonRequest;
                if (verifyEchoString(req)) {
                    ctx.end(req.getEchostr());
                } else {
                    ctx.end("success");
                }
            } else {
                echoListeners.forEach(e -> e.call((EchoRequest) commonRequest, ctx));
            }
        } else if (commonRequest instanceof MessageRequest) {
            MessageRequest msgReq = (MessageRequest) commonRequest;
            log.info("received message request -> {}", msgReq.toString());

            String fromXML = ctx.getStringBody();
            try {
                WXBizMsgCrypt pc = new WXBizMsgCrypt(getWechatToken(), getAesKey(), getAppId());
                String decryptedXml = pc.decryptMsg(msgReq.getMsgSignature(), msgReq.getTimestamp().toString(), msgReq.getNonce(), fromXML);
                log.info("received message -> {}", decryptedXml);

                CommonMessage commonMessage = MessageXmlUtils.parseXml(decryptedXml, CommonMessage.class);
                Optional.ofNullable(commonMessage).map(CommonMessage::getMsgType).ifPresent(msgType -> {
                    switch (msgType) {
                        case "text": {
                            if (textMessageListeners.isEmpty()) {
                                defaultReplyMessage(ctx, msgReq);
                            } else {
                                TextMessage textMessage = MessageXmlUtils.parseXml(decryptedXml, TextMessage.class);
                                textMessageListeners.forEach(e -> e.call(msgReq, textMessage, ctx));
                            }
                        }
                        break;
                        case "image": {
                            if (imageMessageListeners.isEmpty()) {
                                defaultReplyMessage(ctx, msgReq);
                            } else {
                                ImageMessage imageMessage = MessageXmlUtils.parseXml(decryptedXml, ImageMessage.class);
                                imageMessageListeners.forEach(e -> e.call(msgReq, imageMessage, ctx));
                            }
                        }
                        break;
                        case "voice": {
                            if (voiceMessageListeners.isEmpty()) {
                                defaultReplyMessage(ctx, msgReq);
                            } else {
                                VoiceMessage voiceMessage = MessageXmlUtils.parseXml(decryptedXml, VoiceMessage.class);
                                voiceMessageListeners.forEach(e -> e.call(msgReq, voiceMessage, ctx));
                            }
                        }
                        break;
                        case "video": {
                            if (videoMessageListeners.isEmpty()) {
                                defaultReplyMessage(ctx, msgReq);
                            } else {
                                VideoMessage videoMessage = MessageXmlUtils.parseXml(decryptedXml, VideoMessage.class);
                                videoMessageListeners.forEach(e -> e.call(msgReq, videoMessage, ctx));
                            }
                        }
                        break;
                        case "location": {
                            if (locationMessageListeners.isEmpty()) {
                                defaultReplyMessage(ctx, msgReq);
                            } else {
                                LocationMessage locationMessage = MessageXmlUtils.parseXml(decryptedXml, LocationMessage.class);
                                locationMessageListeners.forEach(e -> e.call(msgReq, locationMessage, ctx));
                            }
                        }
                        break;
                        case "link": {
                            if (linkMessageListeners.isEmpty()) {
                                defaultReplyMessage(ctx, msgReq);
                            } else {
                                LinkMessage linkMessage = MessageXmlUtils.parseXml(decryptedXml, LinkMessage.class);
                                linkMessageListeners.forEach(e -> e.call(msgReq, linkMessage, ctx));
                            }
                        }
                        break;
                        case "event": {
                            EventMessage eventMessage = MessageXmlUtils.parseXml(decryptedXml, EventMessage.class);
                            Optional.ofNullable(eventMessage).map(EventMessage::getEvent).ifPresent(evt -> {
                                switch (evt) {
                                    case "subscribe": {
                                        if (subscribedMessageListeners.isEmpty()) {
                                            defaultReplyMessage(ctx, msgReq);
                                        } else {
                                            subscribedMessageListeners.forEach(e -> e.call(msgReq, eventMessage, ctx));
                                        }
                                    }
                                    break;
                                    case "unsubscribe": {
                                        if (unsubscribedMessageListeners.isEmpty()) {
                                            defaultReplyMessage(ctx, msgReq);
                                        } else {
                                            unsubscribedMessageListeners.forEach(e -> e.call(msgReq, eventMessage, ctx));
                                        }
                                    }
                                    break;
                                    case "SCAN": {
                                        if (scanMessageListeners.isEmpty()) {
                                            defaultReplyMessage(ctx, msgReq);
                                        } else {
                                            scanMessageListeners.forEach(e -> e.call(msgReq, eventMessage, ctx));
                                        }
                                    }
                                    break;
                                    case "CLICK": {
                                        if (clickMessageListeners.isEmpty()) {
                                            defaultReplyMessage(ctx, msgReq);
                                        } else {
                                            clickMessageListeners.forEach(e -> e.call(msgReq, eventMessage, ctx));
                                        }
                                    }
                                    break;
                                    case "VIEW": {
                                        if (viewMessageListeners.isEmpty()) {
                                            defaultReplyMessage(ctx, msgReq);
                                        } else {
                                            viewMessageListeners.forEach(e -> e.call(msgReq, eventMessage, ctx));
                                        }
                                    }
                                    break;
                                    case "LOCATION": {
                                        if (reportMessageListeners.isEmpty()) {
                                            defaultReplyMessage(ctx, msgReq);
                                        } else {
                                            ReportLocationMessage reportLocationMessage = MessageXmlUtils.parseXml(decryptedXml, ReportLocationMessage.class);
                                            reportMessageListeners.forEach(e -> e.call(msgReq, reportLocationMessage, ctx));
                                        }
                                    }
                                    break;
                                }
                            });

                        }
                        break;
                    }
                });
            } catch (Exception e) {
                log.error("decrypt message exception", e);
            }
        } else {
            if (otherRequestListeners.isEmpty()) {
                ctx.end("success");
            } else {
                otherRequestListeners.forEach(e -> e.call(ctx));
            }
        }
    }

    protected void defaultReplyMessage(RoutingContext ctx, MessageRequest msgReq) {
        String replyXml = encryptMessage("success", System.currentTimeMillis() / 1000, msgReq.getNonce());
        log.info("reply message -> {}", replyXml);
        ctx.end(replyXml);
    }

    @Override
    public void addTextMessageListener(Action3 action) {
        textMessageListeners.add(action);
    }

    @Override
    public void addImageMessageListener(Action3 action) {
        imageMessageListeners.add(action);
    }

    @Override
    public void addVoiceMessageListener(Action3 action) {
        voiceMessageListeners.add(action);
    }

    @Override
    public void addVideoMessageListener(Action3 action) {
        videoMessageListeners.add(action);
    }

    @Override
    public void addLocationMessageListener(Action3 action) {
        locationMessageListeners.add(action);
    }

    @Override
    public void addLinkMessageListener(Action3 action) {
        linkMessageListeners.add(action);
    }

    @Override
    public void addSubscribedMessageListener(Action3 action) {
        subscribedMessageListeners.add(action);
    }

    @Override
    public void addUnsubscribedMessageListener(Action3 action) {
        unsubscribedMessageListeners.add(action);
    }

    @Override
    public void addScanMessageListener(Action3 action) {
        scanMessageListeners.add(action);
    }

    @Override
    public void addClickMessageListener(Action3 action) {
        clickMessageListeners.add(action);
    }

    @Override
    public void addViewMessageListener(Action3 action) {
        viewMessageListeners.add(action);
    }

    @Override
    public void addReportLocationMessageListener(Action3 action) {
        reportMessageListeners.add(action);
    }

    @Override
    public void addEchoStringListener(Action2 action) {
        echoListeners.add(action);
    }

    @Override
    public void addOtherRequestListener(Action1 action) {
        otherRequestListeners.add(action);
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    @Override
    public boolean verifyEchoString(EchoRequest request) {
        Assert.hasText(request.getEchostr(), "The echo string must be not empty");
        Assert.hasText(request.getSignature(), "The signature string must be not empty");
        Assert.hasText(request.getNonce(), "The nonce string must be not empty");
        Assert.notNull(request.getTimestamp(), "The timestamp must be not null");
        Assert.isTrue(request.getTimestamp() > 0, "The timestamp string must be greater than 0");

        TreeSet set = new TreeSet<>(Arrays.asList(request.getNonce(), request.getTimestamp().toString(), wechatToken));
        StringBuilder sign = new StringBuilder();
        set.forEach(sign::append);
        try {
            String originSign = sign.toString();
            String hexSign = HexUtils.bytesToHex(MessageDigest.getInstance("SHA-1").digest(originSign.getBytes(StandardCharsets.US_ASCII)));
            log.info("verify echo string. {} | {} | {}", originSign, hexSign, request.getSignature());
            return request.getSignature().equals(hexSign);
        } catch (Exception e) {
            log.error("verify echo string exception", e);
            return false;
        }
    }

    @Override
    public String encryptMessage(String reply, Long timeStamp, String nonce) {
        try {
            WXBizMsgCrypt pc = new WXBizMsgCrypt(getWechatToken(), getAesKey(), getAppId());
            return pc.encryptMsg(reply, timeStamp.toString(), nonce);
        } catch (Exception e) {
            log.error("encrypt message exception", e);
            return null;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy