Slack Lead Scanner
Monitor #leadscanner for new images/text, process as leads via Apollo + HubSpot, reply in-thread. Also check Slack DM replies to update HubSpot contacts.
Setup
- - SLACK_TOKEN in ~/.openclaw/credentials/slack-bot-token (xoxb- token)
- Apollo key in ~/.openclaw/credentials/apollo-api-key
- HubSpot token in ~/.openclaw/credentials/hubspot-api-key
- State file: ~/clawd/memory/slack-lead-scanner-state.json
- Portal ID: 43856876
- Channel: #leadscanner (C0AQAJ8VD8A)
State File Schema
CODEBLOCK0
Polling Workflow
Part 1: Process New #leadscanner Messages
- 1. Load state from ~/clawd/memory/slack-lead-scanner-state.json
- Fetch messages: INLINECODE0
- Filter: ts > lastts, not in processedids, has files or non-empty text
- For each new message:
a. Download image:
curl -H "Authorization: Bearer $SLACK_TOKEN" "[file.url_private_download]" -o /tmp/lead-[ts].jpg
b. Analyze with image tool: "Extract structured data from this badge or business card. Return JSON: {first
name, lastname, company, title, email, phone, notes}"
c. Append message.text to notes
d. Dedup: Search HubSpot first by firstname+lastname+company — if exists, skip enrich, go to step (g)
e. Enrich via Apollo:
curl -X POST -H "Content-Type: application/json" -d '{"api_key":"[KEY]","first_name":"[f]","last_name":"[l]","organization_name":"[co]","title":"[t]","reveal_personal_emails":true,"reveal_phone_number":true}' https://api.apollo.io/api/v1/people/match
f. Sleep 10s, then search HubSpot for new contact (sort by createdate DESC, filter name+company)
g. Get contact id, build link:
https://app.hubspot.com/contacts/43856876/contact/[id]
h. Send DM to Mark (user:U03H7C6HW5B) with formatted message (see Reply Format below)
i. Track DM timestamp from response, add to pending
dmreplies in state
j. React ✅ on original #leadscanner message
k. Add msg ts to processed
ids, update lastts
l. Cleanup: INLINECODE4
Part 2: Check DM Replies for HubSpot Updates
- 5. For each entry in pendingdmreplies:
a. Fetch thread replies:
curl -H "Authorization: Bearer $SLACK_TOKEN" "https://slack.com/api/conversations.replies?channel=[DM_CHANNEL_ID]&ts=[dm_ts]"
b. Filter replies that are from the user (not the bot itself) and not yet processed
c. Parse reply text as a natural-language HubSpot update instruction, e.g.:
- "update title to VP of Sales" → jobtitle
- "wrong email, it's john@acme.com" → email
- "company is Acme Corp" → company
- "phone is 415-555-1234" → phone
- "add note: met at SaaStr" → note (create HubSpot note via /crm/v3/objects/notes)
d. Apply update via HubSpot PATCH:
curl -X PATCH -H "Authorization: Bearer $HUBSPOT_TOKEN" -H "Content-Type: application/json" -d '{"properties":{"[field]":"[value]"}}' "https://api.hubapi.com/crm/v3/objects/contacts/[hubspot_contact_id]"
e. Reply in thread: "✅ Updated [field] to [value] for [contact_name]."
f. If unrecognized instruction, reply: "❓ I didn't understand that. Try: 'update title to [value]' or 'email is [value]'"
- 6. Write updated state back to ~/clawd/memory/slack-lead-scanner-state.json
- Log to memory/YYYY-MM-DD.md
Reply Format (DM to Mark)
Use Slack line breaks (\n) between fields:
CODEBLOCK1
HubSpot Field Mappings
| Natural language | HubSpot property |
|---|
| name / first name / last name | firstname / lastname |
| title / job title / role |
jobtitle |
| company / org | company |
| email | email |
| phone | phone |
| note / notes | create note object |
Error Handling
- - No Apollo match: send DM anyway with extracted data, note "No enrichment match"
- No HubSpot match after sleep: send DM with "Sync pending - check HubSpot later", omit link
- API errors: Log and DM "Error processing: [msg]" with ❌ react
- Unknown DM reply: Reply with ❓ and guidance
Notes
- - Use haiku model for cost efficiency
- Rate limits: Slack ~1 call/sec, Apollo per plan, HubSpot 100 req/10s
- Never log or output tokens
- Get DM channel ID for direct messages: it's a conversations.open response for user U03H7C6HW5B
Slack Lead Scanner
监控 #leadscanner 频道中的新图片/文本,通过 Apollo + HubSpot 将其作为潜在客户处理,并在线程中回复。同时检查 Slack 私信回复以更新 HubSpot 联系人。
设置
- - SLACK_TOKEN 位于 ~/.openclaw/credentials/slack-bot-token(xoxb- 令牌)
- Apollo 密钥位于 ~/.openclaw/credentials/apollo-api-key
- HubSpot 令牌位于 ~/.openclaw/credentials/hubspot-api-key
- 状态文件:~/clawd/memory/slack-lead-scanner-state.json
- 门户 ID:43856876
- 频道:#leadscanner(C0AQAJ8VD8A)
状态文件架构
json
{
last_ts: 1234567890,
processedids: [msgts1, msgts_2],
channel_id: C0AQAJ8VD8A,
pendingdmreplies: [
{
dm_ts: 1234567890.123456,
hubspotcontactid: 12345,
contact_name: John Doe
}
]
}
轮询工作流程
第一部分:处理新的 #leadscanner 消息
- 1. 从 ~/clawd/memory/slack-lead-scanner-state.json 加载状态
- 获取消息:curl -H Authorization: Bearer $SLACKTOKEN https://slack.com/api/conversations.history?channel=C0AQAJ8VD8A&oldest=[lastts]&limit=50
- 过滤条件:ts > lastts,不在 processedids 中,包含文件或非空文本
- 对于每条新消息:
a. 下载图片:curl -H Authorization: Bearer $SLACK
TOKEN [file.urlprivate_download] -o /tmp/lead-[ts].jpg
b. 使用图片工具分析:从这张徽章或名片中提取结构化数据。返回 JSON:{first
name, lastname, company, title, email, phone, notes}
c. 将 message.text 附加到 notes 中
d. 去重:首先通过 firstname+lastname+company 搜索 HubSpot —— 如果存在,跳过丰富步骤,转到步骤 (g)
e. 通过 Apollo 丰富数据:curl -X POST -H Content-Type: application/json -d {api
key:[KEY],firstname:[f],last
name:[l],organizationname:[co],title:[t],reveal
personalemails:true,reveal
phonenumber:true} https://api.apollo.io/api/v1/people/match
f. 等待 10 秒,然后搜索 HubSpot 中的新联系人(按 createdate DESC 排序,过滤 name+company)
g. 获取联系人 ID,构建链接:https://app.hubspot.com/contacts/43856876/contact/[id]
h. 向 Mark(用户:U03H7C6HW5B)发送私信,包含格式化消息(参见下方回复格式)
i. 从响应中跟踪私信时间戳,添加到状态中的 pending
dmreplies
j. 在原始 #leadscanner 消息上回复 ✅ 表情
k. 将消息 ts 添加到 processed
ids,更新 lastts
l. 清理:rm /tmp/lead-[ts].jpg
第二部分:检查私信回复以更新 HubSpot
- 5. 对于 pendingdmreplies 中的每条记录:
a. 获取线程回复:curl -H Authorization: Bearer $SLACK
TOKEN https://slack.com/api/conversations.replies?channel=[DMCHANNEL
ID]&ts=[dmts]
b. 过滤来自用户(非机器人本身)且尚未处理的回复
c. 将回复文本解析为自然语言的 HubSpot 更新指令,例如:
- 将职位更新为销售副总裁 → jobtitle
- 邮箱错误,正确邮箱是 john@acme.com → email
- 公司是 Acme Corp → company
- 电话是 415-555-1234 → phone
- 添加备注:在 SaaStr 会议上认识 → note(通过 /crm/v3/objects/notes 创建 HubSpot 备注)
d. 通过 HubSpot PATCH 应用更新:curl -X PATCH -H Authorization: Bearer $HUBSPOT
TOKEN -H Content-Type: application/json -d {properties:{[field]:[value]}} https://api.hubapi.com/crm/v3/objects/contacts/[hubspotcontact_id]
e. 在线程中回复:✅ 已将 [contact_name] 的 [field] 更新为 [value]。
f. 如果无法识别指令,回复:❓ 我无法理解该指令。请尝试:将职位更新为 [值] 或 邮箱是 [值]
- 6. 将更新后的状态写回 ~/clawd/memory/slack-lead-scanner-state.json
- 记录到 memory/YYYY-MM-DD.md
回复格式(发送给 Mark 的私信)
使用 Slack 换行符(\n)分隔字段:
[全名]
[职位]
[公司]
📧 [邮箱]
📞 [电话]
🔗
回复此消息以更新 HubSpot 中的任何字段。
HubSpot 字段映射
| 自然语言 | HubSpot 属性 |
|---|
| 姓名 / 名 / 姓 | firstname / lastname |
| 职位 / 工作头衔 / 角色 |
jobtitle |
| 公司 / 组织 | company |
| 邮箱 | email |
| 电话 | phone |
| 备注 / 注释 | 创建备注对象 |
错误处理
- - 无 Apollo 匹配:仍发送包含提取数据的私信,注明无丰富匹配
- 等待后无 HubSpot 匹配:发送私信并注明同步待处理 - 稍后检查 HubSpot,省略链接
- API 错误:记录日志并通过 ❌ 表情回复处理错误:[消息]
- 未知私信回复:回复 ❓ 并提供指导
备注
- - 使用 haiku 模型以提高成本效率
- 速率限制:Slack 约 1 次调用/秒,Apollo 按计划,HubSpot 100 次请求/10 秒
- 切勿记录或输出令牌
- 获取直接消息的私信频道 ID:这是针对用户 U03H7C6HW5B 的 conversations.open 响应