Files
agentic-govbot/scripts/publish_constitution.py
Nathan Schneider 54beddb420 Add Slack platform adapter and comprehensive platform skills system
Major Features:
- Implemented full Slack channel-bot adapter with Socket Mode
- Added 35+ Mastodon platform skills across 7 categories
- Created constitution publishing system for Mastodon

Slack Adapter (NEW):
- Full PlatformAdapter implementation (1071 lines)
- Socket Mode for real-time events
- 16 channel-scoped governance skills
- User group management as channel "roles"
- Channel access control and management
- Message pinning and moderation
- Platform limitations documentation
- Comprehensive setup guide (SLACK_SETUP.md)

Mastodon Platform Skills (ENHANCED):
- Account Moderation (9 skills): suspend, silence, disable, mark sensitive, delete status
- Account Management (4 skills): approve, reject, delete data, create account
- Report Management (4 skills): assign, unassign, resolve, reopen
- Federation Management (4 skills): block/unblock domains, allow/disallow federation
- Security Management (4 skills): block IP/email domains
- Constitution Management (2 skills): publish constitution, update profile
- Documented web-only limitations (role management requires tootctl)

Constitution Publishing:
- Publish constitution as pinned Mastodon thread
- Automatic deprecation of previous versions
- Version control with change summaries
- Thread splitting for long documents
- CLI tool: scripts/publish_constitution.py
- Documentation: CONSTITUTION_PUBLISHING.md

Configuration & Integration:
- Added SlackConfig model with bot_token, app_token, channel_id
- Updated PlatformConfig to support type: slack
- Added slack-sdk>=3.33.0 dependency
- Bot.py now routes to Slack adapter
- Updated example config with Slack section

Documentation:
- SLACK_SETUP.md: Complete Slack setup guide
- PLATFORM_SKILLS.md: All 35+ Mastodon skills documented
- CONSTITUTION_PUBLISHING.md: Constitution publishing guide
- Updated README.md: Merged QUICKSTART, added Slack to supported platforms
- Updated PLATFORMS.md: Slack marked as implemented with examples
- Updated .gitignore: Added instance-specific state files

Security:
- All sensitive files properly gitignored
- Instance-specific state (.constitution_post_id) excluded
- Credentials properly handled in config

Breaking Changes: None

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 22:46:48 -07:00

105 lines
3.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Helper script to publish constitution to Mastodon.
Usage:
python scripts/publish_constitution.py [--summary "What changed"]
"""
import sys
import argparse
from pathlib import Path
# Add src to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.govbot.utils.config import load_config
from src.govbot.platforms.mastodon import MastodonAdapter
def main():
parser = argparse.ArgumentParser(description="Publish constitution to Mastodon")
parser.add_argument(
"--summary",
"-s",
default="Constitution updated",
help="Summary of changes made"
)
parser.add_argument(
"--constitution",
"-c",
default="constitution.md",
help="Path to constitution file (default: constitution.md)"
)
parser.add_argument(
"--config",
default="config/config.yaml",
help="Path to config file (default: config/config.yaml)"
)
args = parser.parse_args()
# Load constitution
constitution_path = Path(args.constitution)
if not constitution_path.exists():
print(f"❌ Constitution file not found: {constitution_path}")
sys.exit(1)
constitution_text = constitution_path.read_text()
print(f"📜 Loaded constitution from {constitution_path} ({len(constitution_text)} chars)")
# Load config
try:
config = load_config(args.config)
except FileNotFoundError:
print(f"❌ Config file not found: {args.config}")
print(" Copy config/config.example.yaml to config/config.yaml and configure")
sys.exit(1)
# Connect to Mastodon
print(f"🔌 Connecting to {config.platform.mastodon.instance_url}...")
adapter = MastodonAdapter(config.platform.mastodon.model_dump())
if not adapter.connect():
print("❌ Failed to connect to Mastodon")
sys.exit(1)
print(f"✅ Connected as @{adapter.bot_username}")
# Publish constitution
print(f"\n📤 Publishing constitution...")
print(f" Change summary: {args.summary}")
result = adapter.execute_skill(
skill_name="publish_constitution",
parameters={
"constitution_text": constitution_text,
"change_summary": args.summary,
},
actor="@admin"
)
if result["success"]:
print(f"\n{result['message']}")
print(f"\n📊 Details:")
print(f" - Thread length: {result['data']['thread_length']} posts")
print(f" - First post ID: {result['data']['first_post_id']}")
if result['data'].get('previous_post_id'):
print(f" - Previous post ID: {result['data']['previous_post_id']} (deprecated)")
# Generate profile URL
profile_url = f"{adapter.instance_url}/@{adapter.bot_username}"
print(f"\n🔗 View at: {profile_url}")
else:
print(f"\n❌ Failed: {result['message']}")
sys.exit(1)
# Disconnect
adapter.disconnect()
print("\n✨ Done!")
if __name__ == "__main__":
main()