木子屋 Dnawo's BLOG

钉钉API发送消息到钉钉完整方法图解(附源码)

👤 dnawo 📅 2026-10-17 👁 18 👍 1 💬 0 🔄 本站原创 📂 Web编程
以前尝试用ntfy在手机接收自定义消息,但ntfy常常被关闭,最近发现调用钉钉API可以给钉钉发送文本消息,实现原理是钉钉加入企业后,管理员创建企业应用,然后调用API让应用给企业成员发送消息,成员在钉钉查看消息,这样曲线实现将自定义消息发送到钉钉上,下面分享一下方法。

一、钉钉应用创建和配置

1.1 创建应用,获取AgentId、AppKey和AppSecret

登录钉钉开发者平台,打开应用开发>钉钉应用,创建应用,记下AgentId、AppKey和AppSecret:

图片

图片

1.2 开通发送工作通知权限

调用API发送工作通知需要申请开通权限,这个权限默认是开通的,可以在应用页面进入开发配置>权限管理,确认是否开通:

图片

二、调用API给钉钉发送自定义消息

登录钉钉管理后台,打开通讯录>成员管理,获取成员UserID,和上面获取到的AgentId、AppKey和AppSecret一起填入下面程序即可发送消息:

<?php
declare(strict_types=1);

// Web 访问时输出纯文本(CLI 下不调用 header)
if (PHP_SAPI !== 'cli') {
    header('Content-Type: text/plain; charset=utf-8');
}

// ============================ 配置区:请替换为你的真实值 ============================
const APP_KEY     = '你的AppKey';       // 钉钉开放平台 → 应用详情 → AppKey
const APP_SECRET  = '你的AppSecret';    // 钉钉开放平台 → 应用详情 → AppSecret
const AGENT_ID    = 123456789;          // 微应用的 AgentId(整型)
const TO_USER     = 'userid1,userid2';  // 接收人 userid,多个用英文逗号分隔,最大 100 个
const MSG_CONTENT = '自定义消息';         // 消息内容
// =====================================================================================

// ---------- 1. 获取 access_token ----------
// GET https://oapi.dingtalk.com/gettoken?appkey=APP_KEY&appsecret=APP_SECRET
$token = getAccessToken();
if ($token === null) {
    exit(1);
}
echo "获取 access_token 成功\n";

// ---------- 2. 发送工作通知(文本消息)----------
// POST https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=ACCESS_TOKEN
[$result, $err] = sendTextMessage($token, TO_USER, AGENT_ID, MSG_CONTENT);

if ($err !== null) {
    echo '[asyncsend_v2] ' . $err . "\n";
    exit(1);
}

if (($result['errcode'] ?? -1) === 0) {
    echo '消息发送请求成功,errmsg: ' . ($result['errmsg'] ?? 'ok') . "\n";
    if (!empty($result['task_id'])) {
        echo 'task_id: ' . $result['task_id'] . "\n";
        echo "注意:asyncsend_v2 为异步接口,task_id 仅代表请求已受理,不代表用户一定收到。\n";
    }
    exit(0);
}

echo sprintf(
    "[asyncsend_v2] errcode=%d errmsg=%s\n",
    $result['errcode'] ?? -1,
    $result['errmsg'] ?? ''
);
// 常见失败原因排查提示(钉钉侧常见错误码)
if (($result['errcode'] ?? 0) === 40089) {
    echo "可能原因:userid_list 中存在无效的 userid,或接收人不在应用可见范围内。\n";
}
if (($result['errcode'] ?? 0) === 40014) {
    echo "可能原因:access_token 无效或已过期。\n";
}
if (($result['errcode'] ?? 0) === 60011) {
    echo "可能原因:应用没有发送工作通知的权限,请检查应用权限配置。\n";
}
exit(1);

/**
 * 获取钉钉 access_token
 */
function getAccessToken(): ?string
{
    $url = 'https://oapi.dingtalk.com/gettoken'
        . '?appkey=' . urlencode(APP_KEY)
        . '&appsecret=' . urlencode(APP_SECRET);

    [$data, $err] = httpRequest($url);
    if ($err !== null) {
        echo '[gettoken] ' . $err . "\n";
        return null;
    }
    if (($data['errcode'] ?? 1) !== 0) {
        echo sprintf(
            "[gettoken] errcode=%d errmsg=%s\n",
            $data['errcode'] ?? -1,
            $data['errmsg'] ?? ''
        );
        return null;
    }
    return $data['access_token'] ?? null;
}

/**
 * 异步发送工作通知(文本消息)
 *
 * @return array{0: array|null, 1: string|null} [接口返回的 JSON 数组, 请求错误信息]
 */
function sendTextMessage(string $token, string $toUser, int $agentId, string $content): array
{
    $url = 'https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2'
        . '?access_token=' . urlencode($token);

    $payload = [
        'agent_id'    => $agentId,
        'userid_list' => $toUser,
        'msg'         => [
            'msgtype' => 'text',
            'text'    => ['content' => $content],
        ],
    ];

    return httpRequest($url, 'POST', json_encode($payload, JSON_UNESCAPED_UNICODE));
}

/**
 * 基于 cURL 的 HTTP 请求封装
 *
 * 说明:PHP 8.0+ 的 curl 句柄由引擎自动释放,无需(也不应)调用 curl_close()。
 *
 * @return array{0: array|null, 1: string|null} [解码后的 JSON 数组, 错误信息]
 */
function httpRequest(string $url, string $method = 'GET', ?string $body = null): array
{
    $ch = curl_init($url);

    $options = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
    ];

    // 仅当 cacert.pem 存在且为有效 PEM 文本时才指定 CAINFO;
    // 文件缺失/损坏时跳过,回退到 php.ini 的 curl.cainfo 配置
    $caInfo = __DIR__ . '/cacert.pem';
    if (is_file($caInfo) && filesize($caInfo) > 0
        && strpos((string)file_get_contents($caInfo, false, null, 0, 1024), '-----BEGIN CERTIFICATE-----') !== false
    ) {
        $options[CURLOPT_CAINFO] = $caInfo;
    } else {
        echo '[提示] cacert.pem 缺失或无效,未指定 CAINFO,将依赖 php.ini 的 curl.cainfo;'
            . "若仍报证书错误,请重新下载有效的 cacert.pem\n";
    }

    curl_setopt_array($ch, $options);

    if ($method === 'POST') {
        curl_setopt($ch, CURLOPT_POST, true);
        if ($body !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json; charset=utf-8']);
        }
    }

    $resp = curl_exec($ch);
    if ($resp === false) {
        return [null, 'curl error: ' . curl_error($ch)];
    }
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    $data = json_decode($resp, true);
    if (!is_array($data)) {
        return [null, '响应不是合法 JSON(HTTP ' . $httpCode . '): ' . substr($resp, 0, 200)];
    }
    return [$data, null];
}


最终效果:

图片

👉说明:企业内部应用每天给每个员工最多可发送500条消息通知,单次最多只能给5000人发送,同一员工一天只能发送一条内容相同的消息通知。

相关链接

[1].钉钉开发者平台:https://open-dev.dingtalk.com/
[2].钉钉管理后台:https://oa.dingtalk.com/ (获取UserID)
[3].API文档:https://open.dingtalk.com/document/development/asynchronous-sending-of-enterprise-session-messages

评论(0)

暂无评论。

计算题
评论需审核通过后显示
← 上一篇 企业微信API发送消息到微信完整方法图解(附源码)