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
1 change: 1 addition & 0 deletions docs/features/embed.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ note.comは以下のサービスの埋め込みに対応しています:
| noteマネー(株価チャート) | ✅ | 株価チャートとして埋め込み |
| Zenn.dev | ✅ | 記事カードとして埋め込み |
| Qiita | ✅ | 記事カードとして埋め込み |
| connpass | ✅ | イベントカードとして埋め込み |
| Google Slides | ✅ | プレゼンテーションとして埋め込み |
| SpeakerDeck | ✅ | プレゼンテーションとして埋め込み |
| その他の外部URL | ❌ | 通常のリンクとして表示 |
Expand Down
6 changes: 6 additions & 0 deletions examples/sample_embeds.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ https://zenn.dev/driller/articles/0669cf98e07ffa

https://qiita.com/driller/items/31c1ff4d0bf5813f624f

## イベント

### connpassイベント埋め込み

https://fin-py.connpass.com/event/381982/

## コード

### GitHub Gist埋め込み
Expand Down
14 changes: 12 additions & 2 deletions src/note_mcp/api/embeds.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Embed URL detection and HTML generation for note.com.

This module provides functions for detecting embed URLs (YouTube, Twitter, note.com,
GitHub Gist, GitHub Repository, noteマネー, Zenn.dev, Google Slides, SpeakerDeck, Qiita)
and generating the required HTML structure for note.com embeds.
GitHub Gist, GitHub Repository, noteマネー, Zenn.dev, Google Slides, SpeakerDeck, Qiita,
connpass) and generating the required HTML structure for note.com embeds.

This is the single source of truth for embed URL patterns (DRY principle).

Expand All @@ -24,6 +24,9 @@

Issue #244: Qiita article embed support added. Qiita URLs use 'external-article'
service type (same as Zenn.dev) via the same /v2/embed_by_external_api endpoint.

Issue #254: connpass event embed support added. connpass URLs use 'external-article'
service type (same as Zenn.dev and Qiita) via the same /v2/embed_by_external_api endpoint.
"""

from __future__ import annotations
Expand Down Expand Up @@ -67,6 +70,12 @@
# Example: https://qiita.com/driller/items/31c1ff4d0bf5813f624f (Issue #244)
QIITA_PATTERN = re.compile(r"^https?://qiita\.com/[\w-]+/items/[\w]+$")

# connpass: {group}.connpass.com/event/{event_id}/
# Example: https://fin-py.connpass.com/event/381982/ (Issue #254)
# Note: connpass uses subdomain format for group names
# Note: www subdomain is excluded (connpass canonical URLs use group subdomain)
CONNPASS_PATTERN = re.compile(r"^https?://(?!www\.)([\w-]+)\.connpass\.com/event/\d+/?$")

# GitHub Repository: github.com/owner/repo (with optional trailing slash)
# Example: https://github.com/anthropics/claude-code (Issue #226)
# Note: This pattern must NOT match gist.github.com (handled by GIST_PATTERN)
Expand Down Expand Up @@ -99,6 +108,7 @@
(MONEY_PATTERN, "oembed"),
(ZENN_PATTERN, "external-article"),
(QIITA_PATTERN, "external-article"), # Qiita also uses external-article (Issue #244)
(CONNPASS_PATTERN, "external-article"), # connpass.com events (Issue #254)
]


Expand Down
45 changes: 45 additions & 0 deletions tests/e2e/test_embed_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,48 @@ async def test_stock_notation_in_code_block_not_converted(
finally:
# Clean up created article
await delete_draft_with_retry(real_session, article_key)


class TestConnpassEmbedApiConversion:
"""Test connpass event embed conversion via API (Issue #254)."""

async def test_connpass_embed_via_api(
self,
real_session: Session,
) -> None:
"""connpassイベントURLがAPIでfigure要素に変換される.

- ブラウザを起動せずにAPIのみで下書き作成
- connpassイベントURLがfigure要素に変換される
- embedded-service="external-article"属性が設定される
"""
# Arrange - Use real connpass event URL (fin-py勉強会)
connpass_url = "https://fin-py.connpass.com/event/381982/"
body = f"""connpassイベント埋め込みテスト

{connpass_url}

上記にconnpassイベントカードが表示されます。"""

# Act
result = await note_create_draft.fn(
title="[E2E-TEST] connpass Embed via API",
body=body,
tags=["e2e-test", "embed-api"],
)

# Assert - API response
assert "下書きを作成しました" in result

# Verify embed figure is present in raw HTML
article_key = extract_article_key(result)
try:
article_html = await get_article_html(article_key)

# Verify embed figure is present
assert 'embedded-service="external-article"' in article_html
assert f'data-src="{connpass_url}"' in article_html
assert "embedded-content-key=" in article_html
finally:
# Clean up created article
await delete_draft_with_retry(real_session, article_key)
Loading