Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ TELEGRAM_CHAT_ID=
# ─── Discord ─────────────────────────────────────────────
# 채널 설정 > 연동 > 웹후크에서 발급한 전체 Webhook URL (URL 자체가 시크릿)
DISCORD_WEBHOOK_URL=
# (선택) Bot 토큰 — 채널 직접 전송/조회(create_message·list_messages)용
DISCORD_BOT_TOKEN=

# ─── LINE ────────────────────────────────────────────────
# LINE Developers 콘솔에서 발급한 채널 액세스 토큰
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
- **core**: provenance 강제 테스트 + GitHub Actions CI(pytest·ruff·카탈로그/체인지로그 drift)
- **core**: LICENSE(Apache-2.0)·CONTRIBUTING 추가
- **discord**: Webhook 메시지 전송 MCP 추가 — `discord_send_message`
- **discord**: 핵심 도구 확장 — Webhook 임베드/편집/삭제(`discord_send_embed`·`discord_edit_message`·`discord_delete_message`) + Bot 토큰 경로(`discord_create_message`·`discord_list_messages`, `DISCORD_BOT_TOKEN`)
- **kakao**: '나에게 보내기' MCP 추가 — `kakao_send_text_to_me`, `kakao_send_link_to_me`
- **line**: LINE Messaging API push 텍스트 MCP 추가 — `line_send_text`(전송 메시지 id 반환)
- **line**: push 응답 계약을 공식 스펙(`sentMessages[]`)에 맞게 수정, text 길이를 UTF-16 코드 유닛으로 검증
- **line**: 코어 도구 확장 — `line_reply_text`(reply, sentMessages), `line_multicast_text`(userId 최대 500, 빈 응답), `line_broadcast_text`(빈 응답), `line_get_profile`(Profile 조회) 추가
- **repo**: README 상단 배지(CI · License · Python) 추가, 저장소 public 공개
- **telegram**: sendMessage 기반 telegram_send_message 추가
- **telegram**: 코어 도구 확장 — getMe(헬스체크)/sendPhoto/sendDocument(URL·file_id만)/editMessageText/deleteMessage 추가
<!-- END UNRELEASED -->
32 changes: 30 additions & 2 deletions arcsolve/http.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""모든 서비스가 공유하는 HTTP 호출 + 에러 매핑.

서비스는 여기 동사(post_form / get_json / post_json)를 재사용하고, 직접 httpx 세션을
만들지 않는다. 새 인증 방식(Bearer/API-key 등)은 헤더로 주입한다.
서비스는 여기 동사(post_form / get_json / post_json / patch_json / delete_json)를 재사용하고,
직접 httpx 세션을 만들지 않는다. 새 인증 방식(Bearer/API-key 등)은 헤더로 주입한다.
"""

from __future__ import annotations
Expand Down Expand Up @@ -96,3 +96,31 @@ async def post_json(
return await _request(
"POST", url, headers=headers, json=json, timeout=timeout, transport=transport
)


async def patch_json(
url: str,
*,
headers: dict | None = None,
json: dict | None = None,
timeout: float = DEFAULT_TIMEOUT,
transport: httpx.BaseTransport | None = None,
) -> dict:
"""application/json PATCH → JSON. (리소스 부분 수정, 예: 메시지 편집)"""
return await _request(
"PATCH", url, headers=headers, json=json, timeout=timeout, transport=transport
)


async def delete_json(
url: str,
*,
headers: dict | None = None,
params: dict | None = None,
timeout: float = DEFAULT_TIMEOUT,
transport: httpx.BaseTransport | None = None,
) -> dict:
"""DELETE → JSON(또는 본문 없으면 빈 dict). (리소스 삭제, 예: 메시지 삭제)"""
return await _request(
"DELETE", url, headers=headers, params=params, timeout=timeout, transport=transport
)
77 changes: 57 additions & 20 deletions arcsolve/services/discord/README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,79 @@
# Discord 서비스

Discord **Webhook**으로 채널에 메시지를 전송하는 래퍼.
Discord 채널에 메시지를 보내고(임베드 포함) 편집·삭제하며, Bot 토큰으로 임의 채널에
메시지를 전송·조회하는 래퍼. 두 인증 경로(Webhook / Bot 토큰)를 지원한다.

## 계약 출처 (공식 문서)
- Execute Webhook: https://discord.com/developers/docs/resources/webhook#execute-webhook
- Message Object: https://discord.com/developers/docs/resources/message#message-object
- Edit Webhook Message: https://discord.com/developers/docs/resources/webhook#edit-webhook-message
- Delete Webhook Message: https://discord.com/developers/docs/resources/webhook#delete-webhook-message
- Message Object / Embed: https://discord.com/developers/docs/resources/message#message-object
- Create Message: https://discord.com/developers/docs/resources/message#create-message
- Get Channel Messages: https://discord.com/developers/docs/resources/message#get-channel-messages
- API Base URL / 버전: https://discord.com/developers/docs/reference#api-reference-base-url
- 인증(Bot 토큰 스킴): https://discord.com/developers/docs/reference#authentication
- JSON 에러 코드: https://discord.com/developers/docs/topics/opcodes-and-status-codes#json-json-error-codes
> (위 URL은 `https://docs.discord.com/developers/...`로 301 리다이렉트된다.)
> 계약 본체는 [`contract.py`](contract.py)에 코드로 박제되어 있다(엔드포인트·요청/응답 모델).

## 엔드포인트
| 종류 | METHOD · PATH |
|------|------|
| Execute Webhook | `POST /webhooks/{webhook.id}/{webhook.token}` |
| 종류 | METHOD · PATH | 인증 |
|------|------|------|
| Execute Webhook | `POST /webhooks/{webhook.id}/{webhook.token}` | 없음 (Webhook URL이 시크릿) |
| Edit Webhook Message | `PATCH /webhooks/{webhook.id}/{webhook.token}/messages/{message.id}` | 없음 |
| Delete Webhook Message | `DELETE /webhooks/{webhook.id}/{webhook.token}/messages/{message.id}` | 없음 |
| Create Message | `POST /channels/{channel.id}/messages` | `Authorization: Bot <token>` |
| Get Channel Messages | `GET /channels/{channel.id}/messages?limit=N` | `Authorization: Bot <token>` |

Base: 전체 Webhook URL을 `DISCORD_WEBHOOK_URL`로 통째로 받는다(id/token이 URL path에 포함) ·
인증: **없음** (Webhook URL 자체가 시크릿) · 스코프: 해당 없음
Base(Bot 토큰 경로): `https://discord.com/api/v10` (v10은 현행 "Available" 버전) ·
Webhook 경로: 전체 Webhook URL을 `DISCORD_WEBHOOK_URL`로 통째로 받는다(id/token이 URL path에 포함).

## 인증 (두 경로)
- **Webhook**: 인증 헤더가 없다. Webhook URL 자체가 시크릿이다. `discord_send_message` /
`discord_send_embed` / `discord_edit_message` / `discord_delete_message`가 이 경로를 쓴다.
- **Bot 토큰**: `Authorization: Bot <token>` 헤더(Bearer 아님)를 직접 주입한다.
`discord_create_message` / `discord_list_messages`가 이 경로를 쓴다.

## 셋업
**Webhook 경로**
1. Discord 채널 → **설정 → 연동(Integrations) → 웹후크(Webhooks)**에서 웹후크 생성
2. **웹후크 URL 복사**
3. `.env`에 `DISCORD_WEBHOOK_URL=...` 작성 (인터랙티브 인증 단계 없음)

> 이 서비스는 OAuth가 아니므로 `arcsolve-mcp auth discord`가 없다. URL만 있으면 동작한다.
**Bot 토큰 경로(선택)**
1. [Discord 개발자 포털](https://discord.com/developers/applications)에서 애플리케이션·봇 생성
2. **봇 토큰 발급** 후 대상 길드에 초대(채널 View/Send 권한 부여)
3. `.env`에 `DISCORD_BOT_TOKEN=...` 작성

> 이 서비스는 OAuth가 아니므로 `arcsolve-mcp auth discord`가 없다.
> Webhook 경로는 URL만, Bot 경로는 토큰만 있으면 동작한다.
> `DISCORD_BOT_TOKEN` 미설정 시 Bot 도구는 친절한 설정 안내를 반환한다.

## 도구
| 도구 | 설명 |
|------|------|
| `discord_send_message(content, username?, avatar_url?, tts?)` | 채널에 메시지 전송(≤2000자) |
| 도구 | 경로 | 설명 |
|------|------|------|
| `discord_send_message(content, username?, avatar_url?, tts?)` | Webhook | 채널에 텍스트 전송(≤2000자) |
| `discord_send_embed(title?, description?, url?, color?, footer?)` | Webhook | 리치 임베드 1개 전송(필드 ≥1 필수) |
| `discord_edit_message(message_id, content?)` | Webhook | Webhook 메시지 본문 편집 |
| `discord_delete_message(message_id)` | Webhook | Webhook 메시지 삭제(성공 204) |
| `discord_create_message(channel_id, content)` | Bot | 임의 채널에 텍스트 전송(≤2000자) |
| `discord_list_messages(channel_id, limit?)` | Bot | 채널 최근 메시지 조회(limit 1–100, 기본 50) |

## 범위 / 제약
- MVP는 **Webhook 실행(메시지 전송)만**. Webhook은 인증이 없어 가장 단순하다.
- `content`는 최대 **2000자**(공식 제약). 공식 문서상 `content`/`embeds`/`components`/`file`/`poll`
중 하나는 필수이며, 본 MVP는 `content`만 노출하므로 `content`를 필수로 둔다.
- 도구는 `?wait=true`로 호출해 서버 확인 + 생성된 Message 오브젝트를 받는다(기본 `wait=false`는
`204 No Content`).
- `content`는 최대 **2000자**(공식 제약, Webhook·Create Message 공통). Nitro 적용 시 4000자지만
본 서비스는 표준 2000자 상한을 강제한다.
- `embeds`는 메시지당 **최대 10개**(공식 제약). 본 서비스 `discord_send_embed`는 임베드 1개를 보낸다.
Webhook 임베드는 `type`/`provider`/`video`, 이미지 `height`/`width`/`proxy_url`을 설정할 수 없다(공식).
- `color`는 **RGB 정수**(예: 빨강 `16711680`). `footer`는 텍스트 문자열 1개로 단순화했다.
- 임베드/편집은 표시할 필드가 최소 하나 있어야 한다(없으면 입력 오류 안내).
- 메시지 전송/임베드는 `?wait=true`로 호출해 생성된 Message 오브젝트(특히 `id`)를 받는다
(기본 `wait=false`는 `204 No Content`). 이 `id`로 편집·삭제가 가능하다.
- 편집·삭제는 **동일 Webhook이 보낸 메시지**에만 적용된다.
- `discord_list_messages`의 `limit`은 **1–100**(공식 제약), 범위를 벗어나면 입력 오류 안내.

## 확장 포인트
- `embeds`(임베드 카드, 최대 10개) / `allowed_mentions` / `components` 필드: `contract.py`의
`ExecuteWebhookRequest`에 추가 → `tools.py`에 인자 노출.
- Bot 토큰 경로 [create-message](https://discord.com/developers/docs/resources/message#create-message)
(`Authorization: Bot <token>` 헤더 사용): 임의 채널 전송이 필요하면 별도 도구로 추가.
- `allowed_mentions` / `components` / `attachments`(파일 업로드, multipart): `contract.py`에 모델 추가 →
`tools.py`에 인자 노출. 파일 업로드는 `multipart/form-data`가 필요해 코어 HTTP 확장이 선행돼야 한다.
- 임베드의 `fields`/`author`/`image`/`thumbnail` 등 더 풍부한 하위 모델: `Embed`에 필드 추가.
- Bot 토큰 경로의 메시지 편집/삭제(`PATCH`/`DELETE /channels/{channel.id}/messages/{message.id}`):
필요 시 별도 도구로 추가.
Loading
Loading