Build plugins that integrate external SEO data using SEOMetricsHooks.
Overview
SEO metrics hooks allow plugins to:
- Receive and enrich external SEO data (GSC, Ahrefs, keyword tools)
- Filter data before pattern extraction
- React to discovered patterns
- Track when learnings are applied to content generation
Data Flow:
External API → dvp_seo_metrics_received → Storage
Storage → dvp_before_pattern_extraction → Pattern Mining
Pattern Mining → dvp_pattern_discovered → Notification
Generation → dvp_learning_applied → Tracking
Quick Start
from typing import TYPE_CHECKING
from dvp_cms.plugins.base import Plugin
from dvp_cms.plugins.hookspec import SEOMetricsHooks
if TYPE_CHECKING:
from dvp_cms.plugins.context import HookContext
from dvp_cms.plugins.hooks.seo_metrics import (
SEOMetricsPayload,
DiscoveredPattern,
AppliedLearning,
)
class GSCIntegrationPlugin(Plugin, SEOMetricsHooks):
"""Plugin that integrates Google Search Console data."""
name = "gsc-integration"
version = "1.0.0"
async def dvp_seo_metrics_received(
self,
ctx: "HookContext",
payload: "SEOMetricsPayload",
) -> "SEOMetricsPayload":
"""Enrich incoming SEO metrics data."""
if payload.source == "gsc":
# Calculate CTR from raw data
impressions = payload.data.get("impressions", 0)
clicks = payload.data.get("clicks", 0)
ctr = clicks / impressions if impressions > 0 else 0.0
return payload.with_metadata({
"ctr": ctr,
"enriched_by": self.name,
})
return payload
async def dvp_pattern_discovered(
self,
ctx: "HookContext",
pattern: "DiscoveredPattern",
) -> None:
"""React to discovered patterns."""
if pattern.is_high_confidence:
self.logger.info(
f"High-confidence pattern: {pattern.name}",
extra={"confidence": pattern.confidence},
)
Hooks Reference
dvp_seo_metrics_received
Called when SEO metrics data is received from external sources.
async def dvp_seo_metrics_received(
self,
ctx: "HookContext",
payload: "SEOMetricsPayload",
) -> "SEOMetricsPayload":
"""
Type: Filter hook
Returns: Modified SEOMetricsPayload
"""
if payload.source == "gsc":
# Add computed metrics
return payload.with_metadata({
"ctr": clicks / impressions,
"processed_by": self.name,
})
return payload
dvp_before_pattern_extraction
Called before pattern extraction begins on the data.
async def dvp_before_pattern_extraction(
self,
ctx: "HookContext",
payload: "SEOMetricsPayload",
) -> "SEOMetricsPayload":
"""
Type: Filter hook
Returns: Modified SEOMetricsPayload
"""
data = payload.data
# Filter out low-impression keywords
if "rankings" in data:
filtered = [r for r in data["rankings"] if r.get("impressions", 0) > 100]
return payload.with_data({**data, "rankings": filtered})
return payload
dvp_pattern_discovered
Called when a pattern is discovered from SEO data.
async def dvp_pattern_discovered(
self,
ctx: "HookContext",
pattern: "DiscoveredPattern",
) -> None:
"""
Type: Action hook
Returns: None
"""
if pattern.is_high_confidence:
await self._send_notification(
f"New pattern: {pattern.name}",
confidence=pattern.confidence,
)
dvp_learning_applied
Called when a learning is applied during content generation.
async def dvp_learning_applied(
self,
ctx: "HookContext",
learning: "AppliedLearning",
) -> None:
"""
Type: Action hook
Returns: None
"""
self.logger.info(
f"Applied {learning.pattern_count} patterns to {learning.content_id}",
)