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

io.uouo.wechat.WeChatBot Maven / Gradle / Ivy

The newest version!
package io.uouo.wechat;

import io.uouo.wechat.api.WeChatApi;
import io.uouo.wechat.api.WeChatApiImpl;
import io.uouo.wechat.api.annotation.Bind;
import io.uouo.wechat.api.client.BotClient;
import io.uouo.wechat.api.constant.Config;
import io.uouo.wechat.api.constant.Constant;
import io.uouo.wechat.api.enums.MsgType;
import io.uouo.wechat.api.model.*;
import io.uouo.wechat.exception.WeChatException;
import io.uouo.wechat.utils.DateUtils;
import io.uouo.wechat.utils.OkHttpUtils;
import io.uouo.wechat.utils.WeChatUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 微信机器人
 *
 * @author biezhi
 * @since 2018/1/18
 */
@Slf4j
public class WeChatBot {

    /**
     * 操作微信接口的API
     */
    private WeChatApi api;

    /**
     * 调用HTTP请求的客户端
     */
    private BotClient botClient;

    /**
     * 微信API配置
     */
    private Config config;

    @Getter
    @Setter
    private boolean running;

    /**
     * 登录会话
     */
    @Setter
    private LoginSession session;

    /**
     * 最后一次正常检查时间戳
     */
    @Getter
    private long lastCheckTs;

    /**
     * 接收消息重试次数
     */
    @Getter
    private final int receiveRetryCount = 10;

    /**
     * 待处理的消息队列
     */
    @Getter
    private volatile BlockingQueue messages = new LinkedBlockingQueue<>();

    /**
     * 注解绑定的函数映射
     */
    private final Map> mapping = new HashMap<>(8);

    public WeChatBot(Builder builder) {
        this.config = builder.config;
        this.botClient = builder.botClient;
        this.session = new LoginSession();
        this.init();
    }

    public WeChatBot(Config config) {
        this(new Builder().config(config));
    }

    public Config config() {
        return this.config;
    }

    public LoginSession session() {
        return session;
    }

    public BotClient client() {
        return botClient;
    }

    public WeChatApi api() {
        return this.api;
    }

    public void addMessages(List messages) {
        try {
            if (null == messages || messages.size() == 0) {
                return;
            }
            for (WeChatMessage message : messages) {
                this.messages.put(message);
            }
        } catch (InterruptedException e) {
            log.error("向队列添加 Message 出错", e);
        }
    }

    public boolean hasMessage() {
        return this.messages.size() > 0;
    }

    public WeChatMessage nextMessage() {
        try {
            return this.messages.take();
        } catch (InterruptedException e) {
            log.error("从队列获取 Message 出错", e);
            return null;
        }
    }

    private void init() {
        Method[] methods = this.getClass().getMethods();
        for (Method method : methods) {
            Bind bind = method.getAnnotation(Bind.class);
            if (null == bind) {
                continue;
            }
            if (method.getParameterTypes().length != 1) {
                throw new WeChatException("方法 " + method.getName() + " 参数个数不对,请检查");
            }
            if (!method.getParameterTypes()[0].equals(WeChatMessage.class)) {
                throw new WeChatException("方法 " + method.getName() + " 参数类型不对,请检查");
            }
            MsgType[] msgTypes = bind.msgType();
            for (MsgType msgType : msgTypes) {
                List invokes = mapping.get(msgType);
                if (null == mapping.get(msgType)) {
                    invokes = new ArrayList<>();
                }
                invokes.add(new Invoke(method, Arrays.asList(bind.accountType()), msgType));
                mapping.put(msgType, invokes);
            }
            log.info("绑定消息监听函数 [{}] => {}", method.getName(), msgTypes);
        }
    }

    /**
     * 给文件助手发送消息
     *
     * @param msg 消息内容
     * @return 发送是否成功
     */
    public boolean sendMsgToFileHelper(String msg) {
        return this.api.sendText("filehelper", msg);
    }

    /**
     * 给某个用户发送消息
     *
     * @param name 用户UserName
     * @param msg  消息内容
     * @return 发送是否成功
     */
    public boolean sendMsg(String name, String msg) {
        return this.api.sendText(name, msg);
    }

    /**
     * 根据名称发送消息
     *
     * @param name 备注或昵称,精确匹配
     * @param msg  消息内容
     * @return 发送是否成功
     */
    public boolean sendMsgByName(String name, String msg) {
        Account account = api.getAccountByName(name);
        if (null == account) {
            log.warn("找不到用户: {}", name);
            return false;
        }
        return this.api.sendText(account.getUserName(), msg);
    }

    /**
     * 给某个用户发送图片消息
     *
     * @param name    用户UserName
     * @param imgPath 图片路径
     * @return 发送是否成功
     */
    public boolean sendImg(String name, String imgPath) {
        return this.api.sendImg(name, imgPath);
    }

    /**
     * 根据名称发送图片消息
     *
     * @param name    备注或昵称,精确匹配
     * @param imgPath 图片路径
     * @return 发送是否成功
     */
    public boolean sendImgName(String name, String imgPath) {
        Account account = api.getAccountByName(name);
        if (null == account) {
            log.warn("找不到用户: {}", name);
            return false;
        }
        return this.api.sendImg(account.getUserName(), imgPath);
    }

    /**
     * 给用户发送文件
     *
     * @param name     用户UserName
     * @param filePath 文件路径
     * @return 发送是否成功
     */
    public boolean sendFile(String name, String filePath) {
        return this.api.sendFile(name, filePath);
    }

    /**
     * 根据名称发送文件消息
     *
     * @param name     备注或昵称,精确匹配
     * @param filePath 文件路径
     * @return 发送是否成功
     */
    public boolean sendFileName(String name, String filePath) {
        Account account = api.getAccountByName(name);
        if (null == account) {
            log.warn("找不到用户: {}", name);
            return false;
        }
        return this.api.sendFile(account.getUserName(), filePath);
    }

    /**
     * 启动微信监听
     */
    public void start() {
        this.api = new WeChatApiImpl(this);
        log.info("wechat-bot: {}", Constant.VERSION);
        api.login(config.autoLogin());

        Thread msgHandle = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (hasMessage()) {
                        WeChatMessage weChatMessage = nextMessage();
                        callBack(mapping.get(MsgType.ALL), weChatMessage);
                        callBack(mapping.get(weChatMessage.getMsgType()), weChatMessage);
                    } else {
                        DateUtils.sleep(50);
                    }
                }
            }
        });
        msgHandle.setName("message-handle");
        msgHandle.setDaemon(true);
        msgHandle.start();

        this.other();
    }

    /**
     * 启动后主线程干的事,子类可重写
     */
    protected void other() {
        while (true) {
            Scanner scanner = new Scanner(System.in);
            if (scanner.hasNext()) {
                String text = scanner.next();
                if ("quit".equals(text) || "exit".equals(text)) {
                    api.logout();
                    break;
                }
            }
            DateUtils.sleep(100);
        }
    }

    /**
     * 回调微信消息给客户端、存储器
     *
     * @param invokes 执行器
     * @param message 消息
     */
    private void callBack(List invokes, WeChatMessage message) {
        if (null != invokes && invokes.size() > 0 && null != message) {
            for (Invoke invoke : invokes) {
                invoke.call(this, message);
            }
        }
    }

    /**
     * 更新最后一次正常心跳时间
     */
    public void updateLastCheck() {
        this.lastCheckTs = System.currentTimeMillis();
        writeLoginSession();
    }

    /**
     * 变更自动登陆session
     */
    public void writeLoginSession() {
        if (this.config().autoLogin()) {
            String file = this.config().assetsDir() + "/login.json";
            WeChatUtils.writeJson(file, HotReload.build(this.session()));
            if (log.isDebugEnabled()) {
                log.debug("写入本地登录JSON");
            }
        }
    }

    public static final class Builder {

        private Config config = Config.me();
        private BotClient    botClient;
        private OkHttpClient okHttpClient;

        public Builder() {
            botClient = new BotClient(client(null));
        }

        public Builder okHttpClient(OkHttpClient client) {
            okHttpClient = client;
            return this;
        }

        public Builder config(Config config) {
            this.config = config;
            return this;
        }

        public WeChatBot build() {
            if (okHttpClient != null) {
                botClient = new BotClient(okHttpClient);
            }
            return new WeChatBot(this);
        }

        private static OkHttpClient client(Interceptor interceptor) {
            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            // OkHttpUtils.configureToIgnoreCertificate(builder);
            if (interceptor != null) {
                builder.addInterceptor(interceptor);
            }
            return builder.build();
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy