OpenClaw๋ฅผ โ€œ๋Œ€ํ™”ํ˜• ๋น„์„œโ€์—์„œ ํ•œ ๋‹จ๊ณ„ ์˜ฌ๋ ค, ์‹ค์ œ๋กœ ์ „ํ™”๊นŒ์ง€ ๊ฑฐ๋Š” ์‹คํ–‰ํ˜• ๋น„์„œ๋กœ ๋งŒ๋“œ๋Š” ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์„œ๋Š” ๋ณต์žกํ•œ ์–‘๋ฐฉํ–ฅ ํ†ตํ™”๋ด‡๋ณด๋‹ค, ๋จผ์ € ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์“ฐ๋Š” ์ „ํ™” ์•Œ๋ฆผ ์ž๋™ํ™”์— ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค.

์•ˆ๋‚ด: ๋ณธ๋ฌธ์€ ์ƒ์„ฑํ˜• AI๋ฅผ ํ™œ์šฉํ•ด ์ •๋ฆฌํ–ˆ์œผ๋ฉฐ, ํ† ํฐ/์ „ํ™”๋ฒˆํ˜ธ ๋“ฑ ๋ฏผ๊ฐ์ •๋ณด๋Š” ์˜ˆ์‹œ์—์„œ ๋งˆ์Šคํ‚นํ–ˆ์Šต๋‹ˆ๋‹ค.

flowchart LR
A[OpenClaw ์ด๋ฒคํŠธ ๋ฐœ์ƒ] --> B[์ „ํ™” ๋ธŒ๋ฆฌ์ง€ API ํ˜ธ์ถœ]
B --> C[Twilio Voice API]
C --> D[์ˆ˜์‹ ์ž ์ „ํ™” ์—ฐ๊ฒฐ]
D --> E[TTS ์•Œ๋ฆผ ์žฌ์ƒ]

0) ์ด ๋ฐฉ์‹์ด ์ข‹์€ ์ด์œ 

  • ๋น ๋ฆ„: 10~20๋ถ„์ด๋ฉด ์ฒซ ํ†ตํ™” ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
  • ์•ˆ์ •์ : ์‹ค์‹œ๊ฐ„ ์Œ์„ฑ ๋Œ€ํ™”๋ณด๋‹ค ์žฅ์•  ํฌ์ธํŠธ๊ฐ€ ์ ์Œ
  • ํ™•์žฅ ์‰ฌ์›€: ๋‚˜์ค‘์— ์Šน์ธ/์‹œ๊ฐ„์ œํ•œ/๋‹ค์ค‘ ์ˆ˜์‹ ์ž๋กœ ํ™•์žฅ ๊ฐ€๋Šฅ

1) ์ค€๋น„๋ฌผ

  • Twilio ๊ณ„์ • + Voice ๋ฐœ์‹  ๋ฒˆํ˜ธ 1๊ฐœ
  • OpenClaw๊ฐ€ ๋™์ž‘ ์ค‘์ธ ์„œ๋ฒ„ 1๋Œ€
  • ๋ธŒ๋ฆฌ์ง€ ์„œ๋ฒ„(๊ฐ™์€ ์„œ๋ฒ„ ๊ฐ€๋Šฅ)
  • ํ™˜๊ฒฝ๋ณ€์ˆ˜ 4๊ฐœ
    • TWILIO_ACCOUNT_SID
    • TWILIO_AUTH_TOKEN
    • TWILIO_FROM_NUMBER (์˜ˆ: +1...)
    • CALL_BRIDGE_TOKEN (๋ธŒ๋ฆฌ์ง€ ํ˜ธ์ถœ์šฉ ๋น„๋ฐ€ ํ† ํฐ)

1-1) ๋ฐœ์‹ ๋ฒˆํ˜ธ๋Š” ์–ด๋–ป๊ฒŒ ๋ฐ›๋‚˜? (Twilio์—์„œ ๋ฐœ๊ธ‰ ๊ฐ€๋Šฅ)

๋„ค, Twilio์—์„œ ๋ฐœ์‹ ๋ฒˆํ˜ธ๋ฅผ ์ง์ ‘ ๋ฐœ๊ธ‰(๊ตฌ๋งค)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋น ๋ฅธ ์ˆœ์„œ:

  1. Twilio ๊ณ„์ • ์ƒ์„ฑ(Trial ๊ฐ€๋Šฅ)
  2. Console์—์„œ Phone Number ๊ฒ€์ƒ‰/๊ตฌ๋งค
  3. Voice ๊ฐ€๋Šฅ ๋ฒˆํ˜ธ์ธ์ง€ ํ™•์ธ ํ›„ ๋ฐœ๊ธ‰
  4. ๋ฐœ๊ธ‰๋œ ๋ฒˆํ˜ธ๋ฅผ TWILIO_FROM_NUMBER์— ์„ค์ •

์‹ค๋ฌด ํŒ:

  • Trial ๊ณ„์ •์€ ๊ธฐ๋Šฅ ์ œํ•œ์ด ์žˆ์„ ์ˆ˜ ์žˆ์–ด, ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๋ฒˆํ˜ธ ๊ฒ€์ฆ(Verified Caller ID)์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ตญ๊ฐ€/๋ฒˆํ˜ธ ์œ ํ˜•(์ง€์—ญ๋ฒˆํ˜ธยท์ˆ˜์‹ ์ž ๋ถ€๋‹ด)์— ๋”ฐ๋ผ ๊ทœ์ œ ๋ฌธ์„œ(์ฃผ์†Œ/์‹ ๋ถ„) ์ œ์ถœ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ  ๋งํฌ:

2) ์ตœ์†Œ ๋ธŒ๋ฆฌ์ง€ API ๋งŒ๋“ค๊ธฐ (FastAPI ์˜ˆ์‹œ)

์•„๋ž˜ ์˜ˆ์‹œ๋Š” OpenClaw/์Šคํฌ๋ฆฝํŠธ์—์„œ POST๋ฅผ ๋ฐ›์œผ๋ฉด Twilio๋กœ ์ „ํ™”๋ฅผ ๊ฑฐ๋Š” ์ตœ์†Œ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค.

# file: call_bridge.py
import os
from fastapi import FastAPI, Header, HTTPException
from twilio.rest import Client
 
app = FastAPI()
client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"])
 
ALLOWLIST = {x.strip() for x in os.environ.get("CALL_ALLOWLIST", "").split(",") if x.strip()}
BRIDGE_TOKEN = os.environ["CALL_BRIDGE_TOKEN"]
 
@app.post("/call")
def make_call(payload: dict, authorization: str = Header(default="")):
    if authorization != f"Bearer {BRIDGE_TOKEN}":
        raise HTTPException(status_code=401, detail="unauthorized")
 
    to = payload.get("to", "").strip()
    msg = payload.get("message", "์•Œ๋ฆผ์ด ๋„์ฐฉํ–ˆ์Šต๋‹ˆ๋‹ค.").strip()
 
    if not to:
        raise HTTPException(status_code=400, detail="to required")
    if ALLOWLIST and to not in ALLOWLIST:
        raise HTTPException(status_code=403, detail="number not allowed")
 
    twiml = f"<Response><Say language='ko-KR'>{msg}</Say></Response>"
    call = client.calls.create(
        to=to,
        from_=os.environ["TWILIO_FROM_NUMBER"],
        twiml=twiml,
    )
    return {"ok": True, "callSid": call.sid}

์‹คํ–‰:

uvicorn call_bridge:app --host 0.0.0.0 --port 8787

3) OpenClaw์—์„œ ์ „ํ™” ํŠธ๋ฆฌ๊ฑฐํ•˜๊ธฐ

๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ์‹์€ OpenClaw ์ž‘์—… ํ๋ฆ„(์Šคํฌ๋ฆฝํŠธ/cron/์ˆ˜๋™ ์‹คํ–‰)์—์„œ ๋ธŒ๋ฆฌ์ง€ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

curl -X POST http://127.0.0.1:8787/call \
  -H "Authorization: Bearer $CALL_BRIDGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+82xxxxxxxxxx",
    "message": "๋˜์šฐ๋‹˜, 30๋ถ„ ๋’ค ๋ฏธํŒ… ์‹œ์ž‘์ž…๋‹ˆ๋‹ค."
  }'

4) ์‹ค๋ฌด ์•ˆ์ „์žฅ์น˜ (ํ•„์ˆ˜)

  1. ๋ฒˆํ˜ธ allowlist: ์ง€์ • ๋ฒˆํ˜ธ๋งŒ ๋ฐœ์‹  ํ—ˆ์šฉ
  2. ์‹œ๊ฐ„ ์ œํ•œ: ์‹ฌ์•ผ(์˜ˆ: 22:00~08:00) ๋ฐœ์‹  ๊ธˆ์ง€
  3. ์ฟจ๋‹ค์šด: ๊ฐ™์€ ๋ฒˆํ˜ธ ์—ฐ์† ๋ฐœ์‹  ์ตœ์†Œ ๊ฐ„๊ฒฉ(์˜ˆ: 10๋ถ„)
  4. ์‹คํŒจ fallback: ํ†ตํ™” ์‹คํŒจ ์‹œ Telegram/๋ฌธ์ž ์•Œ๋ฆผ ์ „ํ™˜

5) ํ…Œ์ŠคํŠธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • ๋ธŒ๋ฆฌ์ง€ API ํ—ฌ์Šค์ฒดํฌ(200) ํ™•์ธ
  • ํ—ˆ์šฉ ๋ฒˆํ˜ธ๋กœ ํ…Œ์ŠคํŠธ ์ฝœ ์„ฑ๊ณต
  • allowlist ๋ฏธ๋“ฑ๋ก ๋ฒˆํ˜ธ ์ฐจ๋‹จ ํ™•์ธ
  • ์•ผ๊ฐ„ ์ฐจ๋‹จ/์ฟจ๋‹ค์šด ๋™์ž‘ ํ™•์ธ
  • ์‹คํŒจ ์‹œ ๋Œ€์ฒด ์•Œ๋ฆผ(ํ…”๋ ˆ๊ทธ๋žจ) ํ™•์ธ

6) ๋‹ค์Œ ํ™•์žฅ

  • ๋‹จ์ˆœ TTS ์•Œ๋ฆผ โ†’ โ€œ์Šน์ธ ํ•„์š”/ํ™•์ธ ํ•„์š”โ€ ์Œ์„ฑ ๋ฉ”๋‰ด(ํ‚คํŒจ๋“œ ์ž…๋ ฅ)
  • ๋‹จ์ผ ๋ฒˆํ˜ธ โ†’ ํŒ€ ๋‹จ์œ„ ๋ผ์šฐํŒ…
  • ์ด๋ฒคํŠธ๋ณ„ ์šฐ์„ ์ˆœ์œ„(๊ธด๊ธ‰๋งŒ ์ „ํ™”, ์ผ๋ฐ˜์€ ๋ฉ”์‹œ์ง€)

ํ•œ ์ค„ ๊ฒฐ๋ก 

Twilio ์—ฐ๋™์€ OpenClaw๋ฅผ โ€œ๋ง ์ž˜ํ•˜๋Š” AIโ€์—์„œ ์‹ค์ œ๋กœ ๊นจ์›Œ์ฃผ๋Š” ์‹คํ–‰ํ˜• ๋น„์„œ๋กœ ๋ฐ”๊พธ๋Š” ๊ฐ€์žฅ ์ฒด๊ฐ ํฐ ํ™•์žฅ์ž…๋‹ˆ๋‹ค.

๊ด€๋ จ ๋ฌธ์„œ