阿里云首页 智能外呼机器人

快速集成

引入SDK依赖

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-outboundbot</artifactId>
    <version>RELEASE</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.5.17</version>
</dependency>

获取实例ID和话术ID

参照用户指南创建外呼实例和外呼话术后,在话术详情页面可以获取到实例ID和话术ID。参见下图:

创建任务组

e/**
 *  任务组(JobGroup)是任务的逻辑分组,可以根据业务实际情况,按照时间或者人群等维度划分.
 */
public class CreateJobGroup {
    public static void main(String[] args) throws ClientException {
        DefaultProfile profile = DefaultProfile.getProfile("cn-shanghai", "<accessKeyId>", "<accessSecret>");
        IAcsClient client = new DefaultAcsClient(profile);
        CreateJobGroupRequest request = new CreateJobGroupRequest();
        request.setJobGroupName("第一个作业组");
        request.setInstanceId("<InstanceId>");
        request.setScriptId("<ScriptId>");
        request.setStrategyJson(JSON.toJSONString(createStrategy()));
        try {
            CreateJobGroupResponse response = client.getAcsResponse(request);
            System.out.println(JSON.toJSONString(response));
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            System.out.println("ErrCode:" + e.getErrCode());
            System.out.println("ErrMsg:" + e.getErrMsg());
            System.out.println("RequestId:" + e.getRequestId());
        }
    }
    /**
     * AssignJobs接口也可以传入Strategy参数.
     * Strategy优先级为: AssignJobs > CreateJobGroup.
     * @return
     */
    private static JSONObject createStrategy() {
        JSONObject strategy = new JSONObject();
        strategy.put("maxAttemptsPerDay", 3); //号码每天最大呼叫次数
        strategy.put("minAttemptInterval", 10); //未接通情况下的重试时间间隔,单位为分钟
        strategy.put("name", UUID.randomUUID().toString()); //策略名称
        strategy.put("startTime", System.currentTimeMillis()); //开始时间戳
        strategy.put("endTime", System.currentTimeMillis() + 60 * 60 * 1000L); //结束时间戳
        List<JSONObject> workingTimes = new ArrayList<>(); // 可呼出的时间窗口,24小时制
        JSONObject workingTime0 = new JSONObject();
        workingTime0.put("beginTime", "09:00:00");
        workingTime0.put("endTime", "12:00:00");
        JSONObject workingTime1 = new JSONObject();
        workingTime1.put("beginTime", "14:00:00");
        workingTime1.put("endTime", "16:00:00");
        workingTimes.add(workingTime0);
        workingTimes.add(workingTime1);
        strategy.put("workingTime", workingTimes); 
        return strategy;
    }
}

批量下发任务

/**
 * 可使用AssignJobs循环多次向同一任务组下发任务,每次下发任务数建议25个job.
 */
public class AssignJobs {
    public static void main(String[] args) {
        DefaultProfile profile = DefaultProfile.getProfile("cn-shanghai", "<accessKeyId>", "<accessSecret>");
        IAcsClient client = new DefaultAcsClient(profile);
        AssignJobsRequest request = new AssignJobsRequest();
        request.setInstanceId("<InstanceId>");
        request.setJobGroupId("<JobGroupId>");
        request.setJobsJsons(createJobsJson());
        try {
            AssignJobsResponse response = client.getAcsResponse(request);
            System.out.println(JSON.toJSONString(response));
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            System.out.println("ErrCode:" + e.getErrCode());
            System.out.println("ErrMsg:" + e.getErrMsg());
            System.out.println("RequestId:" + e.getRequestId());
        }
    }
    /**
     *  JobsJson.N是个List,每个JobJson对应一个联系人(contact)。
     * @return
     */
    private static List<String> createJobsJson() {
        JSONObject contact = new JSONObject();
        contact.put("phoneNumber", "135*********");
        contact.put("honorific", "张先生");
        contact.put("name", "张三");
        contact.put("referenceId", UUID.randomUUID().toString());
        List<JSONObject> extras = new ArrayList<>();
        extras.add(new JSONObject().fluentPut("key", "自定义参数1").fluentPut("value", "1"));
        extras.add(new JSONObject().fluentPut("key", "自定义参数2").fluentPut("value", "2"));
        JSONObject job = new JSONObject();
        job.put("contacts", Collections.singletonList(contact));
        job.put("extras", extras);
        List<String> jobs = new ArrayList<>();
        jobs.add(JSON.toJSONString(job));
        return jobs;
    }
}

任务完成消息

对于每个外呼任务的呼叫完成状态,智能外呼服务会通过消息队列(RocketMQ)的方式通知给调用方。消息中会返回任务Id,外呼任务的状态(接通/无人接听/呼叫失败)等。

消息队列提供两种方式:

  1. 外呼公共消息队列。接入方订阅消息。外呼系统推送消息到外呼功能消息队列。此时需要提供外呼业务id,外呼技术支持后台进行配置。一个工作日之内返回消息队列配置信息,用于订阅消息。
  2. 接入方在阿里云购买的RocketMQ服务。接入方订阅消息。接入方提供授权推送消息的权限子账号信息、消息队列的Topic和GroupId。外呼技术支持后台进行配置,配置成功后外呼系统将推送消息到指定的Topic。

数据安全考虑,建议采用第二种。

RocketMQ的购买参见其产品详情页:产品详情页

订阅RocketMQ消息参见RocketMQ的帮助文档:帮助文档

消息类型

消息级别 消息类型 EventType
任务 任务完成 JobComplete
通话 开始呼叫 Dialing
通话 接通通话 CallAnswered
通话 通话结束:挂机或者未接通超时 CallFinished
录音 录音生成成功 RecordingReady

任务级别/通话级别消息体:

{
    "eventType":"JobComplete",
    "eventTime":1588850490334,
    "instanceId":"08a4c460-****-737e069a154f",
    "groupId":"8869f35d-****-9b46437bc0fa",
    "id":"08a4c460-****-737e069a154f",
    "status":"Failed",
    "failureReason":"NoAnswer",
    "extras":[
        {
            "key":"custom",
            "value":"aaa"
        }
    ],
    "contacts":[
        {
            "id":"762c2e16-****-00a0f13372b1",
            "honorific":"test",
            "name":"test",
            "phoneNumber":"11111111",
            "referenceId":"762c2e16-****-00a0f13372b1"
        }
    ],
    "tasks":[
        {
            "id":"60b48efe-****-5d6b70479641",
            "callId":"60b48efe-****-5d6b70479641",
            "planedTime":1588850490334,
            "actualTime":1588850490334,
            "endTime":1588850490334,
            "callingNumber":"123456",
            "calledNumber":"11111111",
            "status":"NoAnswer"
        },
        {
            "id":"60b48efe-****-5d6b70479642",
            "callId":"60b48efe-****-5d6b70479642",
            "planedTime":1588850590334,
            "actualTime":1588850590334,
            "endTime":1588850590334,
            "callingNumber":"123456",
            "calledNumber":"11111111",
            "status":"NoAnswer"
        }
    ]
}

录音级别消息体:

{
    "eventType":"RecordingReady",
    "eventTime":1588850490334,
    "instanceId":"08a4c460-***-737e069a154f",
    "groupId":"8869f35d-***-9b46437bc0fa",
    "id":"08a4c460-***-737e069a154f",
    "extras":[
        {
            "key":"custom",
            "value":"aaa"
        }
    ],
    "contacts":[
        {
            "id":"762c2e16-***-00a0f13372b1",
            "honorific":"test",
            "name":"test",
            "phoneNumber":"11111111",
            "referenceId":"762c2e16-***-00a0f13372b1"
        }
    ],
    "recording":{
        "taskId":"60b48efe-***-5d6b70479641",
        "callId":"60b48efe-***-5d6b70479641",
        "duration":5,
        "fileName":"a.wav",
        "mkvUrl":"https://a.wav",
        "url":"https://a.wav"
    }
}

枚举值:

# EventType #
任务级别
- JobComplete 任务完成
通话级别
- Dialing 开始呼叫
- CallAnswered 通话接通
- CallFinished 通话结束,挂机或者未接通超时
录音级别
- RecordingReady 录音生成成功
# JobStatus #
- Scheduling(0) 调度中
- Executing(1) 执行中
- Succeeded(2) 成功
- Paused(3) 挂起
- Failed(4) 失败
- Cancelled(5) 已取消
# FailureReason #
- Unknown(0) 未知错误
- NoAnswer(1) 无人接听
- InvalidStrategy(2) 无效的策略,策略配置不正确
- TimeUp(3) 调度时发现超时
- NoStrategy(4) 策略为空或没有找到
- CallFailed(5) 呼叫失败
- PerDayCallCountLimit(6) 全局级别的号码每日呼叫次数限制
- ContactBlockList(7) 禁止外呼名单
- EmptyNumber(8) 空号不再外呼
- JobPerDayCallCountLimit(9) 任务级别的号码每日呼叫次数限制
- VerificationCancelled(10) 呼叫前验证不通过取消
- ContactSuspended(11) 止呼
# TaskStatus  #
- Executing(0) 正在拨打
- Succeeded(1) 成功
- NoAnswer(2) 无人接听
- NotExist(3)  联系人号码不存在
- Busy(4) 占线
- Cancelled(5) 已经取消
- Failed(6) 呼叫失败
- NotConnected(7) 无法接通
- PoweredOff(8) 关机
- OutOfService(9) 停机
- InArrears(10) 欠费
- EmptyNumber(11) 空号
- PerDayCallCountLimit(12) 超出每日呼叫限制
- ContactBlockList(13) 禁止外呼名单
- CallerNotRegistered(14) 主叫号码未在中继注册
- Terminated(15) 被终止
- VerificationCancelled(16) 呼叫前校验不通过取消