洛塔服务号回复008获取代码。
功能说明
服务号每个用户可以收到4条群发消息,超过这个数量的会发送失败。
- 所有使用到media_id的地方,现在都可以使用素材管理中的永久素材media_id了。请但注意,使用同一个素材群发出去的链接是一样的,这意味着,删除某一次群发,会导致整个链接失效。
- 对于群发和预览接口中的图文消息 (mpnews) ,请使用通过 “草稿箱 / 新建草稿” 接口获得的 media_id
群发时候不用担心会真的一下子发送出去,如果是全部用户接收,需要管理员进行确认的。也可以进行关闭,路径: 设置 - 安全中心 - 风险操作保护
准备工作
- 公众号后台设置ip白名单
位置:设置与开发–>基本配置,右侧IP白名单
上传图文消息内的图片获取URL
图文消息内的图片,必须使用该接口上传后获得图片url,而不能直接使用其他域名访问到的url。
另外,点击图片进入小程序部分,对应的图片也必须是该接口上传的,不能是自有域名/其他网络域名对应的图片url。
// 先获取access_token,这部分正式环境需要配置定时获取,每天2000次调用限制
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APPID + "&secret=" + SECRET;
String result = Jsoup.connect(url).ignoreContentType(true).method(Method.GET).execute().body();
System.out.println(result);
String accessToken = JSON.parseObject(result).getString("access_token");
// 上传图文消息内的图片获取URL
url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=" + accessToken;
File file = new File("/Users/lootaa/Desktop/1080864.jpg");
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST)
.data("media", file.getName(), new FileInputStream(file))
.timeout(60000).execute().body();
String imgUrl = JSON.parseObject(result).getString("url");
System.out.println(imgUrl);
上传图文消息素材
代码先上传了一下临时素材,作为上传图文消息的封面。该素材可以是临时的,也可以是永久的。
// 新增临时素材,作为上传图文消息素材的封面。type也可以用thumb
url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=" + accessToken + "&type=image";
file = new File("/Users/lootaa/Desktop/234.jpg");
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST)
.data("media", file.getName(), new FileInputStream(file))
.timeout(60000).execute().body();
System.out.println(result);
String mediaId = JSON.parseObject(result).getString("media_id");
得到封面以后,进行上传图文消息素材
- 如果开通了微信支付,content中可以使用a标签,没有开通微信支付的不能使用。
- 插入的图片image标签对应url必须是上传图文消息内的图片获取URL,网络图片不能用
- 插入的小程序有三种方式:
a.卡片小程序,图片尺寸必须是1080*864,其他尺寸的都会发送失败
b.文字进入小程序
c.图片进入小程序,和文字的对比,多了一个img标签,图片必须是上传图文消息内的图片获取URL
url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token=" + accessToken;
JSONObject param = new JSONObject();
JSONArray articles = new JSONArray(); //消息素材列表
JSONObject article1 = new JSONObject(); //第一篇消息素材
article1.put("thumb_media_id", mediaId); //图文消息缩略图的media_id,可以在素材管理 - 新增临时素材中获得
article1.put("author", "lootaa"); // 图文消息的作者,非必填
article1.put("title", "欢迎使用洛塔"); // 图文消息的标题
article1.put("content_source_url", ""); // “阅读原文”后的页面,非必填
article1.put("content", "具有支付能力的可以使用a标签。点击<a href=''>这里</a>体验<br>"
+ "图片<image src=\"" + imgUrl + "\"></image><br>"
+ "点击卡片进入小程序 <br><mp-miniprogram data-miniprogram-appid=\"wxa3b096d8546b270d\" data-miniprogram-path=\"pages/station/station\" data-miniprogram-title=\"爱盯箱小程序\" data-miniprogram-imageurl=\"" + imgUrl + "\"></mp-miniprogram><br>"
+ "点击文字进入小程序 <br><p><a data-miniprogram-appid=\"wxa3b096d8546b270d\" data-miniprogram-path=\"pages/station/station\" href=\"\">点击文字跳转小程序</a></p><br>"
+ "点击图片进入小程序 <br><p><a data-miniprogram-appid=\"wxa3b096d8546b270d\" data-miniprogram-path=\"pages/station/station\" href=\"\"><img src=\"" + imgUrl + "\" alt=\"\" data-width=\"null\" data-ratio=\"NaN\"></a></p><br>"); // 正文
article1.put("digest", "欢迎访问博客,期待您意见反馈。"); // 图文消息的描述,如本字段为空,则默认抓取正文前64个字
article1.put("show_cover_pic", "1"); // 是否显示封面,1为显示,0为不显示
article1.put("need_open_comment", "1"); // 是否打开评论,0不打开,1打开
article1.put("only_fans_can_comment", "0"); // 是否粉丝才可评论,0所有人可评论,1粉丝才可评论
articles.add(article1);
根据标签进行群发
请求参数可以添加clientmsgid,这个参数的作用是用来发送成功后接收推送消息。如果这个值相同,收到的推送就还是之前的,可以避免重复发送推送。
下面的代码都没加这个参数,可选的。
标签可以使用标签部分接口获取到,也可以后台先创建,并移动粉丝,然后用接口获取到对应id。
鉴于每个月只能给自己发送4条测试,所以仅仅真实跑通了图文和文字两种的,其他的未经过真实代码测试。如果要体验效果,可以用接下来几部分里面的预览接口。
- 发送图文消息:mpnews
- 发送文本:text
- 发送语音/音频:voice
- 发送图片:images
- 发送视频:mpvideo(这个流程较多,可参考下述代码)
- 发送卡券:wxcard(使用卡券部分接口或者后台先创建得到card_id)
// 根据标签进行群发(请求参数也可以添加clientmsgid,用来避免重复发送推送。下例中不添加该参数)
url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=" + accessToken;
param = new JSONObject();
// a. 发送图文消息
JSONObject filter = new JSONObject();
filter.put("is_to_all", false); //是否发送所有人
filter.put("tag_id", "2"); //标签id,可以根据标签管理中相关接口获取。也可以后台创建一个标签,然后接口只做个查询,查询出来的id填这里测试
param.put("filter", filter);
JSONObject mpnews = new JSONObject();
mpnews.put("media_id", newsMediaId); //图文消息media_id
param.put("mpnews", mpnews);
param.put("msgtype", "mpnews"); //类型,mpnews为图文消息
param.put("send_ignore_reprint", 0); //图文消息被判定为转载时,是否继续群发。 1为继续群发(转载),0为停止群发。 该参数默认为0。
// // b.发送文本
// JSONObject filter = new JSONObject();
// filter.put("is_to_all", false); //是否发送所有人
// filter.put("tag_id", "2"); //标签id,可以根据标签管理中相关接口获取。也可以后台创建一个标签,然后接口只做个查询,查询出来的id填这里测试
// param.put("filter", filter);
// JSONObject text = new JSONObject();
// text.put("content", "这里是文本内容"); //图文消息media_id
// param.put("text", text);
// param.put("msgtype", "text"); //类型
// // c.发送语音/音频
// JSONObject filter = new JSONObject();
// filter.put("is_to_all", false); //是否发送所有人
// filter.put("tag_id", "2"); //标签id,可以根据标签管理中相关接口获取。也可以后台创建一个标签,然后接口只做个查询,查询出来的id填这里测试
// param.put("filter", filter);
// JSONObject voice = new JSONObject();
// voice.put("media_id", "新增临时素材得到的media_id"); //素材管理->新增素材来得到(也可以接收用户语音,得到media_id)
// param.put("voice", voice);
// param.put("msgtype", "text"); //类型
// // d.图片
// JSONObject filter = new JSONObject();
// filter.put("is_to_all", false); //是否发送所有人
// filter.put("tag_id", "2"); //标签id,可以根据标签管理中相关接口获取。也可以后台创建一个标签,然后接口只做个查询,查询出来的id填这里测试
// param.put("filter", filter);
// JSONObject images = new JSONObject(); //消息素材列表
// JSONArray imageArray = new JSONArray();
// imageArray.add("图片素材的media_id");
// imageArray.add("图片素材的media_id,可以发送多个");
// images.put("media_ids", imageArray);
// images.put("recommend", "推荐语,不填则默认为“分享图片”"); //推荐语,不填则默认为“分享图片”
// images.put("need_open_comment", "1"); //是否打开评论,0不打开,1打开
// images.put("only_fans_can_comment", "1"); //是否粉丝才可评论,0所有人可评论,1粉丝才可评论
// param.put("images", images);
// param.put("msgtype", "image"); //类型
// // e.视频,这个流程略微复杂
// // e1. 通过素材管理->新增素材,得到视频的media_id,这个略过
// // e2. 将e1获取到media_id作为参数,调用下述接口,得到可以发送消息的media_id
// String videoUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo?access_token=" + accessToken;
// JSONObject videoParam = new JSONObject();
// videoParam.put("media_id", "e1步骤得到的media_id");
// videoParam.put("title", "标题");
// videoParam.put("description", "描述");
// String videoResult = Jsoup.connect(videoUrl).ignoreContentType(true).method(Method.POST)
// .requestBody(videoParam.toString())
// .timeout(60000).execute().body();
// System.out.println(videoResult);
// String vodeoMediaId = JSON.parseObject(videoResult).getString("media_id");
// // e3. 将e2中的media_id作为参数进行发送
// JSONObject filter = new JSONObject();
// filter.put("is_to_all", false); //是否发送所有人
// filter.put("tag_id", "2"); //标签id,可以根据标签管理中相关接口获取。也可以后台创建一个标签,然后接口只做个查询,查询出来的id填这里测试
// param.put("filter", filter);
// JSONObject mpvideo = new JSONObject();
// mpvideo.put("media_id", vodeoMediaId);
// param.put("mpvideo", mpvideo);
// param.put("msgtype", "mpvideo"); //类型
// // f.卡券消息:需要先利用卡券接口创建并得到对应的card_id
// JSONObject filter = new JSONObject();
// filter.put("is_to_all", false); //是否发送所有人
// filter.put("tag_id", "2"); //标签id,可以根据标签管理中相关接口获取。也可以后台创建一个标签,然后接口只做个查询,查询出来的id填这里测试
// param.put("filter", filter);
// JSONObject wxcard = new JSONObject(); //消息素材列表
// wxcard.put("card_id", "卡券接口获取到的card_id");
// param.put("wxcard", wxcard);
// param.put("msgtype", "wxcard"); //类型
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).requestBody(param.toString()).execute().body();
// {"errcode":0,"errmsg":"send job submission success","msg_id":3147483652,"msg_data_id":2247483698}
System.out.println(result);
根据 OpenID 列表群发
和上面根据标签群发很类似,只不过群体不是标签组,而是一批固定的openid。
这里面有个限制:不能只写一个openid,这个长度限制最小是2个。
// 根据 OpenID 列表群发
url = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=" + accessToken;
param = new JSONObject();
// a. 发送图文消息
JSONArray touser = new JSONArray();
touser.add("ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); //接收消息用户的openid,至少添加两个才能群发
touser.add("ohUHp6tQGQMqM-WuHMaCiR5AkY2Q");
param.put("touser", touser);
mpnews = new JSONObject();
mpnews.put("media_id", newsMediaId); //图文消息media_id
param.put("mpnews", mpnews);
param.put("msgtype", "mpnews"); //类型,mpnews为图文消息
param.put("send_ignore_reprint", 0); //图文消息被判定为转载时,是否继续群发。 1为继续群发(转载),0为停止群发。 该参数默认为0。
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).requestBody(param.toString()).execute().body();
System.out.println(result);
// // b.发送文本
// JSONArray touser = new JSONArray();
// touser.add("ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); //接收消息用户的openid,至少添加两个才能群发
// touser.add("ohUHp6tQGQMqM-WuHMaCiR5AkY2Q");
// param.put("touser", touser);
// JSONObject text = new JSONObject();
// text.put("content", "这里是文本内容"); //图文消息media_id
// param.put("text", text);
// param.put("msgtype", "text"); //类型
// // c.发送语音/音频
// JSONArray touser = new JSONArray();
// touser.add("ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); //接收消息用户的openid,至少添加两个才能群发
// touser.add("ohUHp6tQGQMqM-WuHMaCiR5AkY2Q");
// param.put("touser", touser);
// JSONObject voice = new JSONObject();
// voice.put("media_id", "新增临时素材得到的media_id"); //素材管理->新增素材来得到(也可以接收用户语音,得到media_id)
// param.put("voice", voice);
// param.put("msgtype", "text"); //类型
// // d.图片
// JSONArray touser = new JSONArray();
// touser.add("ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); //接收消息用户的openid,至少添加两个才能群发
// touser.add("ohUHp6tQGQMqM-WuHMaCiR5AkY2Q");
// param.put("touser", touser);
// JSONObject images = new JSONObject(); //消息素材列表
// JSONArray imageArray = new JSONArray();
// imageArray.add("图片素材的media_id");
// imageArray.add("图片素材的media_id,可以发送多个");
// images.put("media_ids", imageArray);
// images.put("recommend", "推荐语,不填则默认为“分享图片”"); //推荐语,不填则默认为“分享图片”
// images.put("need_open_comment", "1"); //是否打开评论,0不打开,1打开
// images.put("only_fans_can_comment", "1"); //是否粉丝才可评论,0所有人可评论,1粉丝才可评论
// param.put("images", images);
// param.put("msgtype", "image"); //类型
// // e.视频,这个流程略微复杂
// // e1. 通过素材管理->新增素材,得到视频的media_id,这个略过
// // e2. 将e1获取到media_id作为参数,调用下述接口,得到可以发送消息的media_id
// String videoUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo?access_token=" + accessToken;
// JSONObject videoParam = new JSONObject();
// videoParam.put("media_id", "e1步骤得到的media_id");
// videoParam.put("title", "标题");
// videoParam.put("description", "描述");
// String videoResult = Jsoup.connect(videoUrl).ignoreContentType(true).method(Method.POST)
// .requestBody(videoParam.toString())
// .timeout(60000).execute().body();
// System.out.println(videoResult);
// String vodeoMediaId = JSON.parseObject(videoResult).getString("media_id");
// // e3. 将e2中的media_id作为参数进行发送
// JSONArray touser = new JSONArray();
// touser.add("ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); //接收消息用户的openid,至少添加两个才能群发
// touser.add("ohUHp6tQGQMqM-WuHMaCiR5AkY2Q");
// param.put("touser", touser);
// JSONObject mpvideo = new JSONObject();
// mpvideo.put("media_id", vodeoMediaId);
// param.put("mpvideo", mpvideo);
// param.put("msgtype", "mpvideo"); //类型
// // f.卡券消息:需要先利用卡券接口创建并得到对应的card_id
// JSONArray touser = new JSONArray();
// touser.add("ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); //接收消息用户的openid,至少添加两个才能群发
// touser.add("ohUHp6tQGQMqM-WuHMaCiR5AkY2Q");
// param.put("touser", touser);
// JSONObject wxcard = new JSONObject(); //消息素材列表
// wxcard.put("card_id", "卡券接口获取到的card_id");
// param.put("wxcard", wxcard);
// param.put("msgtype", "wxcard"); //类型
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).requestBody(param.toString()).execute().body();
// {"errcode":0,"errmsg":"send job submission success","msg_id":3147483653,"msg_data_id":2247483704}
System.out.println(result);
删除群发
必须是已经群发出去的才能删除,如果测试的时候,发送得到msg_id就立刻删除一般都会失败,因为此时微信端还没有把消息群发成功。可以等收到消息后或者收到事件推送后再执行删除查看效果。
消息删除后,列表卡片是不变的,点击进入详情后会看到提示的已删除效果。
// 删除群发
url = "https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token=" + accessToken;
param = new JSONObject();
param.put("msg_id", "3147483652"); //必须是已经群发成功的才能删除,发送中状态的删除会失败
param.put("article_idx", 1); //要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填0会删除全部文章
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).requestBody(param.toString()).execute().body();
System.out.println(result);
预览接口
预览接口和根据标签群发、根据openid群发比较相似,只不过这里可以根据openid单条群发,也可以根据微信号进行群发。
// 预览接口
url = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=" + accessToken;
param = new JSONObject();
// a. 发送图文消息
param.put("towxname", "fymod1988"); // 按照微信号预览,和openid选择一个
// param.put("touser", "ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); // 按照openid预览,和微信号选择一个
mpnews = new JSONObject();
mpnews.put("media_id", newsMediaId); //图文消息media_id
param.put("mpnews", mpnews);
param.put("msgtype", "mpnews"); //类型,mpnews为图文消息
// // b.发送文本
// param.put("towxname", "fymod1988"); // 按照微信号预览,和openid选择一个
param.put("touser", "ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); // 按照openid预览,和微信号选择一个
// JSONObject text = new JSONObject();
// text.put("content", "这里是文本内容"); //图文消息media_id
// param.put("text", text);
// param.put("msgtype", "text"); //类型
// // c.发送语音/音频
// param.put("towxname", "fymod1988"); // 按照微信号预览,和openid选择一个
param.put("touser", "ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); // 按照openid预览,和微信号选择一个
// JSONObject voice = new JSONObject();
// voice.put("media_id", "新增临时素材得到的media_id"); //素材管理->新增素材来得到(也可以接收用户语音,得到media_id)
// param.put("voice", voice);
// param.put("msgtype", "text"); //类型
// // d.图片
// param.put("towxname", "fymod1988"); // 按照微信号预览,和openid选择一个
param.put("touser", "ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); // 按照openid预览,和微信号选择一个
// JSONObject images = new JSONObject(); //消息素材列表
// JSONArray imageArray = new JSONArray();
// imageArray.add("图片素材的media_id");
// imageArray.add("图片素材的media_id,可以发送多个");
// images.put("media_ids", imageArray);
// images.put("recommend", "推荐语,不填则默认为“分享图片”"); //推荐语,不填则默认为“分享图片”
// images.put("need_open_comment", "1"); //是否打开评论,0不打开,1打开
// images.put("only_fans_can_comment", "1"); //是否粉丝才可评论,0所有人可评论,1粉丝才可评论
// param.put("images", images);
// param.put("msgtype", "image"); //类型
// // e.视频,这个流程略微复杂
// // e1. 通过素材管理->新增素材,得到视频的media_id,这个略过
// // e2. 将e1获取到media_id作为参数,调用下述接口,得到可以发送消息的media_id
// String videoUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo?access_token=" + accessToken;
// JSONObject videoParam = new JSONObject();
// videoParam.put("media_id", "e1步骤得到的media_id");
// videoParam.put("title", "标题");
// videoParam.put("description", "描述");
// String videoResult = Jsoup.connect(videoUrl).ignoreContentType(true).method(Method.POST)
// .requestBody(videoParam.toString())
// .timeout(60000).execute().body();
// System.out.println(videoResult);
// String vodeoMediaId = JSON.parseObject(videoResult).getString("media_id");
// // e3. 将e2中的media_id作为参数进行发送
// param.put("towxname", "fymod1988"); // 按照微信号预览,和openid选择一个
param.put("touser", "ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); // 按照openid预览,和微信号选择一个
// JSONObject mpvideo = new JSONObject();
// mpvideo.put("media_id", vodeoMediaId);
// param.put("mpvideo", mpvideo);
// param.put("msgtype", "mpvideo"); //类型
// // f.卡券消息:需要先利用卡券接口创建并得到对应的card_id。文档中的card_ext为附件信息,可以不用写
// param.put("towxname", "fymod1988"); // 按照微信号预览,和openid选择一个
param.put("touser", "ohUHp6iaFJq6SISTVwHS5lkb9Pb8"); // 按照openid预览,和微信号选择一个
// JSONObject wxcard = new JSONObject(); //消息素材列表
// wxcard.put("card_id", "卡券接口获取到的card_id");
// param.put("wxcard", wxcard);
// param.put("msgtype", "wxcard"); //类型
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).requestBody(param.toString()).execute().body();
// {"errcode":0,"errmsg":"send job submission success","msg_id":3147483652,"msg_data_id":2247483698}
System.out.println(result);
查询群发消息发送状态
消息发送后的状态
- SEND_SUCCESS 发送成功
- SENDING 发送中
- SEND_FAIL 发送失败
- DELETE 已删除
// 查询群发消息发送状态
url = "https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token=" + accessToken;
param = new JSONObject();
param.put("msg_id", "3147483652");
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).requestBody(param.toString()).execute().body();
// {"msg_id":3147483652,"msg_status":"SEND_SUCCESS"}
System.out.println(result);
事件推送群发结果
群发消息完成后,会有事件推送,具体接收事件推送部分代码有介绍,可以查看之前的文章。仅需要再接收部分价格判断,如果是群发消息通知,做单独处理。
推送的内容较多,包括状态、每篇文章的成功失败情况、对应的人数等都有。
if(Objects.equals(event, "MASSSENDJOBFINISH")) { // 群发消息
String status = map.get("Status");
System.out.println("状态:" + status);
pw.write(nonce);
}
设置群发速度
设置级别,数字越小表示越快,支持0-4。
speed | realspeed |
0 | 80w/分钟 |
1 | 60w/分钟 |
2 | 45w/分钟 |
3 | 30w/分钟 |
4 | 10w/分钟 |
// 设置群发速度
url = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set?access_token=" + accessToken;
param = new JSONObject();
param.put("speed", "0");
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).requestBody(param.toString()).execute().body();
System.out.println(result);
获取群发速度
获取到的是级别和对应的真实速度
// 获取群发速度
url = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/get?access_token=" + accessToken;
result = Jsoup.connect(url).ignoreContentType(true).method(Method.POST).execute().body();
System.out.println(result);