Add Instagram accounts, Facebook pages, or web aggregators to sources.yaml configuration
This skill inherits all available tools. When active, it can use any tool Claude has access to.
<essential_principles>
All sources are stored in ~/.config/local-media-tools/sources.yaml.
| Type | Identifier | Example |
|---|---|---|
| @handle or handle | @localvenue, elmamm | |
| URL with /events | https://facebook.com/venue/events | |
| Web Aggregator | Any other URL | https://localevents.com |
Instagram Account:
- handle: "localvenue" # Required (@ stripped automatically)
name: "Local Venue" # Auto-generated from handle if omitted
type: "music_venue" # music_venue, bar, restaurant, gallery, promoter, aggregator
location: "Kingston, NY" # Optional
notes: "Live jazz" # Optional
Facebook Page:
- url: "https://facebook.com/venue/events" # Required
name: "The Venue" # Auto-extracted from URL if omitted
Web Aggregator:
- url: "https://localevents.com" # Required
name: "Local Events" # Auto-extracted from domain if omitted
source_type: "listing" # listing, calendar, or venue
max_pages: 50 # 1-200
</essential_principles>
<intake> What sources do you want to add?Examples:
@localvenue @musicbar @artgallery - Add Instagram accountshttps://facebook.com/thevenue/events - Add Facebook pagehttps://hudsonvalleyevents.com - Add web aggregator@venue1, @venue2 and https://events.com - Mix of sourcesProvide the source(s): </intake>
<process> ## Step 1: Parse InputAnalyze the user's input to extract sources:
Instagram detection:
@ → Instagram handleFacebook detection:
facebook.com → Facebook page/events, append itWeb Aggregator detection:
Extract multiple sources:
from pathlib import Path
import yaml
config_path = Path.home() / ".config" / "local-media-tools" / "sources.yaml"
if not config_path.exists():
print("ERROR: sources.yaml not found. Run /newsletter-events:setup first.")
# STOP HERE
with open(config_path) as f:
config = yaml.safe_load(f)
For each source, check if it already exists:
# Instagram
existing_handles = {a["handle"].lower() for a in config["sources"]["instagram"]["accounts"]}
# Facebook
existing_fb_urls = {p["url"].lower() for p in config["sources"]["facebook"]["pages"]}
# Web
existing_web_urls = {s["url"].lower() for s in config["sources"]["web_aggregators"]["sources"]}
For each new source that doesn't exist:
Instagram:
new_account = {
"handle": handle.lstrip("@").lower(),
"name": handle.lstrip("@").replace("_", " ").title(),
"type": "venue", # Default
}
config["sources"]["instagram"]["accounts"].append(new_account)
Facebook:
# Extract name from URL path
path = url.split("facebook.com/")[1].split("/")[0]
new_page = {
"url": url if url.endswith("/events") else f"{url.rstrip('/')}/events",
"name": path.replace("_", " ").replace("-", " ").title(),
}
config["sources"]["facebook"]["pages"].append(new_page)
Web Aggregator:
from urllib.parse import urlparse
domain = urlparse(url).netloc.replace("www.", "")
new_source = {
"url": url,
"name": domain.split(".")[0].replace("-", " ").title(),
"source_type": "listing",
"max_pages": 50,
}
config["sources"]["web_aggregators"]["sources"].append(new_source)
import shutil
from datetime import datetime
backup_path = config_path.with_suffix(f".yaml.{datetime.now():%Y%m%d%H%M%S}.backup")
shutil.copy2(config_path, backup_path)
with open(config_path, "w") as f:
yaml.dump(config, f, default_flow_style=False, sort_keys=False, allow_unicode=True)
from config.config_schema import AppConfig
try:
AppConfig.from_yaml(config_path)
except Exception as e:
# Restore backup
shutil.copy2(backup_path, config_path)
print(f"ERROR: Invalid config. Restored backup. Error: {e}")
# STOP HERE
Display a summary table:
| Type | Source | Name | Status |
|---|---|---|---|
| @localvenue | Local Venue | Added | |
| @musicbar | Music Bar | Already exists | |
| facebook.com/thevenue/events | The Venue | Added |
Footer:
Config saved to ~/.config/local-media-tools/sources.yaml
Backup at sources.yaml.20250120123456.backup
Run /newsletter-events:research to scrape these sources.
</process>
<success_criteria>