洛塔服务号回复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);

微信公众号个人号python群发消息 公众号群发接口_微信小程序

根据 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);

微信公众号个人号python群发消息 公众号群发接口_微信_02

预览接口

预览接口和根据标签群发、根据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);