Karp 的技术博客

目标效果:

钉钉群里发:
create php file User.php

↓

服务器收到消息
↓

调用 AI 生成代码
↓

写入文件 + git commit

↓

钉钉返回执行结果

下面给你一个 Node.js 可直接跑的版本


一、在钉钉创建机器人

1️⃣ 打开钉钉群
2️⃣ 群设置 → 机器人 → 添加机器人
3️⃣ 选择 自定义机器人

你会得到:

  • Webhook URL
  • 加签密钥(secret)

保存这两个。


二、Mac mini 上创建项目

mkdir ding-ai-bot
cd ding-ai-bot
npm init -y
npm install express axios crypto body-parser simple-git

三、完整服务器代码(server.js)

这个版本包含:

  • 接收钉钉消息
  • 校验签名
  • 调用 AI(示例用 Claude API)
  • 写文件
  • git commit
  • 回复钉钉
const express = require('express')
const crypto = require('crypto')
const axios = require('axios')
const bodyParser = require('body-parser')
const fs = require('fs')
const simpleGit = require('simple-git')

const app = express()
app.use(bodyParser.json())

const PORT = 3000

// 你的钉钉 secret
const DING_SECRET = '你的加签secret'
const DING_WEBHOOK = '你的Webhook地址'

// Claude API
const CLAUDE_API_KEY = process.env.ANTHROPIC_API_KEY

// git 实例
const git = simpleGit()

// ====== 验证钉钉签名 ======
function verifySign(timestamp, sign) {
    const stringToSign = `${timestamp}\n${DING_SECRET}`
    const hmac = crypto.createHmac('sha256', DING_SECRET)
    hmac.update(stringToSign)
    const calcSign = hmac.digest('base64')
    return calcSign === sign
}

// ====== 调用 Claude 生成代码 ======
async function callClaude(prompt) {
    const res = await axios.post(
        'https://api.anthropic.com/v1/messages',
        {
            model: "claude-3-sonnet-20240229",
            max_tokens: 800,
            messages: [
                { role: "user", content: prompt }
            ]
        },
        {
            headers: {
                'x-api-key': CLAUDE_API_KEY,
                'anthropic-version': '2023-06-01',
                'Content-Type': 'application/json'
            }
        }
    )

    return res.data.content[0].text
}

// ====== 发送消息到钉钉 ======
async function replyDingTalk(text) {
    await axios.post(DING_WEBHOOK, {
        msgtype: "text",
        text: {
            content: text
        }
    })
}

// ====== 主处理逻辑 ======
app.post('/ding', async (req, res) => {

    const { timestamp, sign } = req.query

    if (!verifySign(timestamp, sign)) {
        return res.status(403).send('sign error')
    }

    const msg = req.body.text?.content

    if (!msg) {
        return res.send('ok')
    }

    try {

        // 示例命令:create php file User.php
        if (msg.startsWith('create php file')) {

            const fileName = msg.split(' ')[3]

            const prompt = `生成一个 ${fileName} PHP 类,包含基础CRUD结构`

            const code = await callClaude(prompt)

            fs.writeFileSync(`./workspace/${fileName}`, code)

            await git.add(`./workspace/${fileName}`)
            await git.commit(`feat: add ${fileName}`)

            await replyDingTalk(`✅ 已生成 ${fileName} 并提交 Git`)
        }

        else {
            await replyDingTalk('未知命令')
        }

        res.send('ok')

    } catch (err) {
        console.error(err)
        await replyDingTalk('❌ 执行失败')
        res.send('error')
    }
})

app.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}`)
})

四、创建 workspace 目录

mkdir workspace

并初始化 git:

cd workspace
git init

五、启动服务

export ANTHROPIC_API_KEY=你的key
node server.js

建议用 pm2:

npm install -g pm2
pm2 start server.js --name ding-ai
pm2 save

六、让钉钉访问到你的 Mac mini

如果在内网,需要:

  • 用云服务器反向代理
  • 或用 frp
  • 或 ngrok

钉钉 webhook 地址要能公网访问:

https://你的域名/ding

七、测试流程

在钉钉群发:

create php file User.php

如果成功:

  • workspace/User.php 被创建
  • git commit 自动完成
  • 群里收到成功消息

八、安全优化(必须做)

1️⃣ 限制只处理 @机器人 消息
2️⃣ 限制用户ID
3️⃣ 限制可操作目录
4️⃣ 禁止执行任意 shell
5️⃣ 限制 token 消耗

否则可能被恶意触发大量 API 费用。


九、如何接入 OpenClaw

如果你想用 OpenClaw 做执行层:

把:

callClaude(prompt)

替换成:

调用 OpenClaw API

即可。

架构变为:

钉钉 → 你的服务器 → OpenClaw → Claude → 执行 → 返回

十、成本控制建议

推荐模型:

  • claude-3-haiku(省钱)
  • claude-3-sonnet(平衡)

不要默认用 Opus。


最终效果

你现在拥有:

一个国内可用的远程 AI 程序员

通过钉钉控制:

  • 写代码
  • 提交 Git
  • 修改文件
  • 运行脚本

全部远程完成。

AI

版权属于:karp
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
更新于: 2026年03月02日 01:26
0

目录

来自 《用 OpenClaw 打造一台“远程 AI 程序员”:从 Mac mini 到 钉钉 自动写代码》