文章目录
- 飞书机器人
- 自定义机器人
- 自定义应用机器人
- 自定义应用发送消息
- 普通文本 text
- 富文本 post
- 图片 image
- 文件 file
- 语音 audio
- 视频 media
- 消息卡片 interactive
- 分享群名片 share_chat
- 分享个人名片 share_user
- 批量发送消息
- 消息加急
- 发送应用内加急
- 发送短信加急
- 发送电话加急
- springboot核心代码实现
给指定用户或者会话一對一发送消息
支持文本、富文本、可交互的消息卡片、群名片、个人名片、图片、视频、音频、文件、表情包
飞书机器人
自定义机器人
自定义机器人是一种只能在当前群聊中使用的机器人。
你可以在 群设置>群机器人 面板中快捷创建一个自定义机器人,无需经过租户管理员审核,即可在当前群聊中通过调用webhook地址的方式完成消息推送。由于自定义机器人无需经过租户管理员审核即可使用,使用上的便捷性也限制了其使用范围:
- 只能在被添加的群聊内使用,不能与机器人单聊
- 只能对群聊进行单向的消息推送,不能调用飞书丰富的开放接口,获取任何用户、租户信息等
自定义应用机器人
应用机器人需要在 开发者后台 中创建,申请发布并经过租户的应用管理员审核通过后,即可使用。在应用发布范围内的用户,可以直接与你创建的机器人单聊,或在 群设置>群机器人 面板中将这个机器人添加进群聊使用。应用机器人受本租户的应用管理员管控。在应用管理员进行权限审核后,应用机器人可以调用飞书丰富的开放接口,获取、使用用户和租户资源。
PS:应用机器人需要提前申请-管理员审核
自定义应用发送消息
普通文本 text
{
"receive_id": "ou_7d8a6e6df7621556ce0d21922b676706ccs",
"content": "{\"text\":\" test content\"}", // 文本内容
"msg_type": "text"
}
// 如果需要文本中进行换行,需要增加转义
{
"receive_id": "oc_xxx",
"content": "{\"text\":\"firstline \\n second line \"}",
"msg_type": "text"
}
// 文本消息@用法说明,user_id字段需填入open_id,union_id 或 user_id来@指定人
{
"receive_id": "oc_xxx",
"content": "{\"text\":\"<at user_id=\\\"ou_xxxxxxx\\\"></at> text content\"}",
"msg_type": "text"
}
富文本 post
富文本可以在一条消息中同时支持文字、At、图片、视频、超链接等元素。
一个富文本可分多个段落(由多个[]组成),每个段落可由多个元素组成,每个元素由tag和相应的字段描述组成。主要图片元素必须是独立的一个段落。
{
"receive_id": "oc_820faa21d7ed275b53d1727a0feaa917",
"content": "{\"zh_cn\":{\"title\":\"我是一个标题\",\"content\":[[{\"tag\":\"text\",\"text\":\"第一行 :\"},{\"tag\":\"a\",\"href\":\"http://www.feishu.cn\",\"text\":\"超链接\"},{\"tag\":\"at\",\"user_id\":\"ou_1avnmsbv3k45jnk34j5\",\"user_name\":\"tom\"}],[{\"tag\":\"img\",\"image_key\":\"img_7ea74629-9191-4176-998c-2e603c9c5e8g\"}],[{\"tag\":\"text\",\"text\":\"第二行:\"},{\"tag\":\"text\",\"text\":\"文本测试\"}],[{\"tag\":\"img\",\"image_key\":\"img_7ea74629-9191-4176-998c-2e603c9c5e8g\"}]]}}",
"msg_type": "post"
}
图片 image
{
"receive_id": "oc_xxx",
"content": "{\"image_key\": \"img_7ea74629-9191-4176-998c-2e603c9c5e8g\"}",
// 图片Key,可通过 上传图片 接口获取image_key。
"msg_type": "image"
}
文件 file
{
"receive_id": "oc_820faa21d7ed275b53d1727a0feaa917",
"content": "{\"file_key\":\"file_v2_xxx\"}",
// 文件Key,可通过上传文件接口获取文件的 file_key。
"msg_type": "file"
}
语音 audio
{
"receive_id": "oc_xxx",
"content": "{\"file_key\":\"file_v2_xxx\"}",
// 语音文件Key,可通过上传文件接口获取音频文件的 file_key。
"msg_type": "audio"
}
视频 media
{
"receive_id": "oc_xxx",
"content": "{\"file_key\":\"file_v2_xxx\",\"image_key\":\"img_v2_xxx\"}",
// 视频文件Key,可通过上传文件接口获取视频文件的 file_key。
// 视频封面图片Key,可通过上传图片接口获取图片的image_key。
"msg_type": "media"
}
消息卡片 interactive
务必注意content消息内容的构建!!
// 使用卡片JSON发送
{
"receive_id": "oc_820faa21d7ed275b53d1727a0feaa917",
"content": "{\"config\":{\"wide_screen_mode\":true},\"elements\":[{\"alt\":{\"content\":\"\",\"tag\":\"plain_text\"},\"img_key\":\"img_7ea74629-9191-4176-998c-2e603c9c5e8g\",\"tag\":\"img\"},{\"tag\":\"div\",\"text\":{\"content\":\"你是否曾因为一本书而产生心灵共振,开始感悟人生?\\n你有哪些想极力推荐给他人的珍藏书单?\\n\\n加入 **4·23 飞书读书节**,分享你的**挚爱书单**及**读书笔记**,**赢取千元读书礼**!\\n\\n📬 填写问卷,晒出你的珍藏好书\\n😍 想知道其他人都推荐了哪些好书?马上[入群围观](https://open.feishu.cn/)\\n📝 用[读书笔记模板](https://open.feishu.cn/)(桌面端打开),记录你的心得体会\\n🙌 更有惊喜特邀嘉宾 4月12日起带你共读\",\"tag\":\"lark_md\"}},{\"actions\":[{\"tag\":\"button\",\"text\":{\"content\":\"立即推荐好书\",\"tag\":\"plain_text\"},\"type\":\"primary\",\"url\":\"https://open.feishu.cn/\"},{\"tag\":\"button\",\"text\":{\"content\":\"查看活动指南\",\"tag\":\"plain_text\"},\"type\":\"default\",\"url\":\"https://open.feishu.cn/\"}],\"tag\":\"action\"}],\"header\":{\"template\":\"turquoise\",\"title\":{\"content\":\"📚晒挚爱好书,赢读书礼金\",\"tag\":\"plain_text\"}}}",
"msg_type": "interactive"
}
// 使用卡片模板template_id发送
{
"receive_id": "ou_7d8a6exxxxccs",
"msg_type": "interactive",
"content": "{\"type\": \"template\", \"data\": { \"template_id\": \"ctp_xxxxxxxxxxxx\", \"template_variable\": {\"article_title\": \"这是文章标题内容\"} } }"
}
分享群名片 share_chat
{
"receive_id": "oc_xxx",
"content": "{\"chat_id\":\"oc_xxx\"}", // 群ID,获取方式请参见群ID 说明。
"msg_type": "share_chat"
}
分享个人名片 share_user
{
"receive_id": "oc_820faa21d7ed275b53d1727a0feaa917",
"content": "{\"user_id\":\"ou_xxx\"}",// 只支持Open ID,且机器人必须对该用户可见,用户的Open ID获取方式请参见了解更多:如何获取 Open ID。
"msg_type": "share_user"
}
批量发送消息
给多个用户或者多个部门中的成员发送消息。
注意事项:
应用需要启用机器人能力
接口权限说明:
必须拥有获取与发送单聊、群组消息权限,或者以应用的身份发消息权限
至少拥有一个批量发送消息权限:
给用户发送需要拥有 给多个用户批量发消息 权限
给部门成员发送需要拥有 给一个或多个部门的成员批量发消息 权限
通过该接口发送的消息 不支持更新以及回复等操作
只能发送给用户,无法发送给群组
异步接口,会有一定延迟,每个应用待发送的消息按顺序处理,请合理安排批量发送范围和顺序。
单个应用每天通过该接口发送的总消息条数不超过50万
消息加急
发送应用内加急
对指定消息进行应用内加急
注意事项:
需要开启机器人能力
不支持加急批量消息
只能加急机器人自己发送的消息
加急时机器人需要在加急消息所在的群中
加急用户的未读加急总数不能超过200条
发送短信加急
对指定消息进行应用内加急与短信加急
注意事项:
需要开启机器人能力
只能加急机器人自己发送的消息
加急时机器人仍需要在加急消息所在的群组中
加急用户的未读加急总数不能超过200条
通过接口产生的短信加急将消耗企业的加急额度,请慎重调用
发送电话加急
对指定消息进行应用内加急与电话加急。
注意事项:
需要开启机器人能力
只能加急机器人自己发送的消息
加急时机器人需要在加急消息所在的群组中
加急用户的未读加急总数不能超过200条
通过接口产生的电话加急将消耗企业的加急额度,请慎重调用
springboot核心代码实现
/*
* Copyright © 2020-2021 Nimbus Corporation All rights reserved.
*
* 使本项目源码前请仔细阅读以下协议内容,如果你同意以下协议才能使用本项目所有的功能,
* 否则如果你违反了以下协议,有可能陷入法律纠纷和赔偿,作者保留追究法律责任的权利.
*
* 1、本代码为商业源代码,只允许已授权内部人员查看使用
* 2、任何人员无权将代码泄露或者授权给其他未被授权人员使用
* 3、任何修改请保留原始作者信息,不得擅自删除及修改
*
* 请保留以上版权信息,否则作者将保留追究法律责任.
*/
package com.nimbus.messagecenter.util;
import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.lark.oapi.Client;
import com.lark.oapi.service.contact.v3.model.BatchGetIdUserReq;
import com.lark.oapi.service.contact.v3.model.BatchGetIdUserReqBody;
import com.lark.oapi.service.contact.v3.model.BatchGetIdUserResp;
import com.lark.oapi.service.contact.v3.model.BatchGetIdUserRespBody;
import com.lark.oapi.service.contact.v3.model.UserContactInfo;
import com.lark.oapi.service.im.v1.model.CreateMessageReq;
import com.lark.oapi.service.im.v1.model.CreateMessageReqBody;
import com.lark.oapi.service.im.v1.model.CreateMessageResp;
import com.lark.oapi.service.im.v1.model.UrgentAppMessageReq;
import com.lark.oapi.service.im.v1.model.UrgentAppMessageResp;
import com.lark.oapi.service.im.v1.model.UrgentPhoneMessageReq;
import com.lark.oapi.service.im.v1.model.UrgentPhoneMessageResp;
import com.lark.oapi.service.im.v1.model.UrgentReceivers;
import com.lark.oapi.service.im.v1.model.UrgentSmsMessageReq;
import com.lark.oapi.service.im.v1.model.UrgentSmsMessageResp;
import com.nimbus.common.data.model.api.ApiResultDto;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* FeiShuUtil.
*
* @author zhupeng
* @version 1.0.0, 2024/4/9
* @since 2024/4/9
*/
@Slf4j
public class FeiShuUtil {
/**
* 通过手机号获取飞书用户信息.
*
* @param phones 手机号
* @param userIdType 用户ID类型,openid或user_id.
* @param client 飞书客户端
* @return 用户信息列表
*/
public static List<UserContactInfo> getFeiShuUsersByPhones(List<String> phones, String userIdType, Client client) {
List<UserContactInfo> userContactInfos = new ArrayList<>();
// 批量查询最多一次查询50个
List<List<String>> splitPhones = Lists.partition(phones, 50);
for (List<String> phone : splitPhones) {
BatchGetIdUserReq req = BatchGetIdUserReq.newBuilder()
.userIdType(userIdType)
.batchGetIdUserReqBody(BatchGetIdUserReqBody.newBuilder().mobiles(phone.toArray(new String[0])).build())
.build();
BatchGetIdUserResp resp;
try {
resp = client.contact().user().batchGetId(req);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!resp.success()) {
log.error("code:{}, msg:{}, regId:{}", resp.getCode(), resp.getMsg(), resp.getRequestId());
}
BatchGetIdUserRespBody data = resp.getData();
if (Objects.nonNull(data)) {
userContactInfos.addAll(List.of(data.getUserList()));
}
}
return userContactInfos;
}
/**
* 给指定用户发飞书消息.
*
* @param receiveId 消息接收人:飞书用户id或飞书群号
* @param content 消息内容
* @param msgType 消息类型
* @param receiveIdType 消息接收者id类型:open_id/user_id/union_id/email/chat_id
* @param client 飞书客户端
* @return 消息发送结果
*/
public static ApiResultDto sendFeiShuMessage(String receiveId, String content, String msgType, String receiveIdType, Client client) {
CreateMessageReq req = CreateMessageReq.newBuilder()
.receiveIdType(receiveIdType)
.createMessageReqBody(CreateMessageReqBody.newBuilder()
.receiveId(receiveId) // 消息接收人
.msgType(msgType) // 消息类型
.content(content) // 消息内容
.build())
.build();
CreateMessageResp resp;
try {
resp = client.im().message().create(req);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!resp.success()) {
log.error("code:{}, msg:{}, regId:{}", resp.getCode(), resp.getMsg(), resp.getRequestId());
return ApiResultDto.failed(resp.getCode(), resp.getMsg());
}
return ApiResultDto.success(resp.getData());
}
/**
* 针对某个消息给某个人进行消息加急,目前使用飞书用户user_id识别用户.
*
* @param messageId 消息id
* @param receiveIds 接收人id
* @param client 飞书客户端
* @return 处理结果
*/
public static ApiResultDto sendUrgentAppMessage(String messageId, List<String> receiveIds, Client client) {
// 发起请求
UrgentAppMessageReq req = UrgentAppMessageReq.newBuilder()
.messageId(messageId)
.userIdType("user_id")
.urgentReceivers(UrgentReceivers.newBuilder().userIdList(receiveIds.toArray(new String[0])).build())
.build();
UrgentAppMessageResp resp;
try {
resp = client.im().message().urgentApp(req);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!resp.success()) {
log.error("code:{}, msg:{}, regId:{}", resp.getCode(), resp.getMsg(), resp.getRequestId());
return ApiResultDto.failed(resp.getCode(), resp.getMsg());
}
return ApiResultDto.success(resp.getData());
}
/**
* 针对某个消息给某个人进行短信加急,目前使用飞书用户user_id识别用户.
*
* @param messageId 消息id
* @param receiveIds 接收人id
* @param client 飞书客户端
* @return 处理结果
*/
public static ApiResultDto sendUrgentSmsMessage(String messageId, List<String> receiveIds, Client client) {
// 发起请求
UrgentSmsMessageReq req = UrgentSmsMessageReq.newBuilder()
.messageId(messageId)
.userIdType("user_id")
.urgentReceivers(UrgentReceivers.newBuilder().userIdList(receiveIds.toArray(new String[0])).build())
.build();
UrgentSmsMessageResp resp;
try {
resp = client.im().message().urgentSms(req);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!resp.success()) {
log.error("code:{}, msg:{}, regId:{}", resp.getCode(), resp.getMsg(), resp.getRequestId());
return ApiResultDto.failed(resp.getCode(), resp.getMsg());
}
return ApiResultDto.success(resp.getData());
}
/**
* 针对某个消息给某个人进行电话加急,目前使用飞书用户user_id识别用户.
*
* @param messageId 消息id
* @param receiveIds 接收人id
* @param client 飞书客户端
* @return 处理结果
*/
public static ApiResultDto sendUrgentPhoneMessage(String messageId, List<String> receiveIds, Client client) {
// 发起请求
UrgentPhoneMessageReq req = UrgentPhoneMessageReq.newBuilder()
.messageId(messageId)
.userIdType("user_id")
.urgentReceivers(UrgentReceivers.newBuilder().userIdList(receiveIds.toArray(new String[0])).build())
.build();
UrgentPhoneMessageResp resp;
try {
resp = client.im().message().urgentPhone(req);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!resp.success()) {
log.error("code:{}, msg:{}, regId:{}", resp.getCode(), resp.getMsg(), resp.getRequestId());
return ApiResultDto.failed(resp.getCode(), resp.getMsg());
}
return ApiResultDto.success(resp.getData());
}
}