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>
This commit is contained in:
@@ -61,7 +61,8 @@ class GovernanceAgent:
|
||||
request: str,
|
||||
actor: str,
|
||||
context: Optional[Dict[str, Any]] = None,
|
||||
platform_skills: Optional[List[Dict[str, Any]]] = None
|
||||
platform_skills: Optional[List[Dict[str, Any]]] = None,
|
||||
platform_limitations: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Process a governance request using agentic interpretation.
|
||||
@@ -80,6 +81,7 @@ class GovernanceAgent:
|
||||
actor: Who made the request
|
||||
context: Optional context (thread ID, etc.)
|
||||
platform_skills: List of available platform-specific skills
|
||||
platform_limitations: Information about what's not possible via API
|
||||
|
||||
Returns:
|
||||
Response dictionary with action taken and audit trail
|
||||
@@ -106,7 +108,8 @@ class GovernanceAgent:
|
||||
memory=memory_context,
|
||||
actor=actor,
|
||||
context=context,
|
||||
platform_skills=platform_skills
|
||||
platform_skills=platform_skills,
|
||||
platform_limitations=platform_limitations
|
||||
)
|
||||
|
||||
# Step 5: Execute the decision
|
||||
@@ -225,7 +228,8 @@ Return your analysis as JSON:
|
||||
memory: Dict[str, Any],
|
||||
actor: str,
|
||||
context: Optional[Dict[str, Any]],
|
||||
platform_skills: Optional[List[Dict[str, Any]]] = None
|
||||
platform_skills: Optional[List[Dict[str, Any]]] = None,
|
||||
platform_limitations: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Use LLM to decide what action to take.
|
||||
@@ -247,10 +251,34 @@ Return your analysis as JSON:
|
||||
platform_skills_info = "\n\nAVAILABLE PLATFORM SKILLS:\n"
|
||||
for skill in platform_skills:
|
||||
params_str = ", ".join([f"{p['name']}: {p['type']}" for p in skill.get('parameters', [])])
|
||||
category = skill.get('category', 'general')
|
||||
platform_skills_info += f"- {skill['name']}({params_str}): {skill['description']}\n"
|
||||
platform_skills_info += f" Category: {category}\n"
|
||||
if skill.get('constitutional_authorization'):
|
||||
platform_skills_info += f" Authorization: {skill['constitutional_authorization']}\n"
|
||||
|
||||
# Format platform limitations for the prompt
|
||||
platform_limitations_info = ""
|
||||
if platform_limitations:
|
||||
platform_limitations_info = "\n\nPLATFORM LIMITATIONS:\n"
|
||||
|
||||
if 'web_interface_only' in platform_limitations:
|
||||
platform_limitations_info += "\nActions requiring web interface (NOT available via bot):\n"
|
||||
for key, desc in platform_limitations['web_interface_only'].items():
|
||||
platform_limitations_info += f"- {key}: {desc}\n"
|
||||
|
||||
if 'not_possible' in platform_limitations:
|
||||
platform_limitations_info += "\nActions not possible via any API:\n"
|
||||
for key, desc in platform_limitations['not_possible'].items():
|
||||
platform_limitations_info += f"- {key}: {desc}\n"
|
||||
|
||||
if 'limitations' in platform_limitations:
|
||||
platform_limitations_info += "\nGeneral limitations to be aware of:\n"
|
||||
for key, desc in platform_limitations['limitations'].items():
|
||||
platform_limitations_info += f"- {key}: {desc}\n"
|
||||
|
||||
platform_limitations_info += "\nIMPORTANT: If a user requests something that requires web interface or is not possible via API, explain this limitation clearly and provide the web interface URL if applicable."
|
||||
|
||||
prompt = f"""You are a governance bot interpreting a community constitution.
|
||||
|
||||
IMPORTANT: When generating the "response" field, use newline characters (\\n) for line breaks:
|
||||
@@ -269,7 +297,7 @@ CURRENT MEMORY STATE:
|
||||
{json.dumps(memory, indent=2)}
|
||||
|
||||
ACTOR: {actor}
|
||||
{platform_skills_info}
|
||||
{platform_skills_info}{platform_limitations_info}
|
||||
Based on the constitution and current state, decide what action to take.
|
||||
|
||||
IMPORTANT AUTHORITY CHECK:
|
||||
|
||||
@@ -25,6 +25,7 @@ from .agent import GovernanceAgent
|
||||
from .scheduler import GovernanceScheduler
|
||||
from .platforms.base import PlatformAdapter, PlatformMessage, MockPlatformAdapter
|
||||
from .platforms.mastodon import MastodonAdapter
|
||||
from .platforms.slack import SlackAdapter
|
||||
|
||||
|
||||
# Configure logging
|
||||
@@ -125,12 +126,14 @@ class Govbot:
|
||||
|
||||
if platform_type == "mastodon":
|
||||
return MastodonAdapter(self.config.platform.mastodon.model_dump())
|
||||
elif platform_type == "slack":
|
||||
return SlackAdapter(self.config.platform.slack.model_dump())
|
||||
elif platform_type == "mock":
|
||||
return MockPlatformAdapter({})
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unknown platform type: {platform_type}. "
|
||||
f"Supported: mastodon, mock"
|
||||
f"Supported: mastodon, slack, mock"
|
||||
)
|
||||
|
||||
def run(self):
|
||||
@@ -199,6 +202,7 @@ class Govbot:
|
||||
|
||||
# Get available platform skills
|
||||
platform_skills_list = []
|
||||
platform_limitations = None
|
||||
try:
|
||||
platform_skills = self.platform.get_skills()
|
||||
# Convert PlatformSkill objects to dicts for the agent
|
||||
@@ -214,6 +218,10 @@ class Govbot:
|
||||
"constitutional_authorization": skill.constitutional_authorization,
|
||||
}
|
||||
platform_skills_list.append(skill_dict)
|
||||
|
||||
# Get platform limitations if available
|
||||
if hasattr(self.platform, 'get_platform_limitations'):
|
||||
platform_limitations = self.platform.get_platform_limitations()
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not get platform skills: {e}")
|
||||
|
||||
@@ -222,6 +230,7 @@ class Govbot:
|
||||
actor=f"@{message.author_handle}",
|
||||
context=context,
|
||||
platform_skills=platform_skills_list if platform_skills_list else None,
|
||||
platform_limitations=platform_limitations,
|
||||
)
|
||||
|
||||
# Check if we need to execute a platform skill
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1071
src/govbot/platforms/slack.py
Normal file
1071
src/govbot/platforms/slack.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,15 @@ class MastodonConfig(BaseModel):
|
||||
bot_username: str = Field("govbot", description="Bot's Mastodon username")
|
||||
|
||||
|
||||
class SlackConfig(BaseModel):
|
||||
"""Slack workspace configuration"""
|
||||
|
||||
bot_token: str = Field(..., description="Slack bot user OAuth token (xoxb-...)")
|
||||
app_token: str = Field(..., description="Slack app-level token for Socket Mode (xapp-...)")
|
||||
channel_id: str = Field(..., description="Channel ID for governance (e.g., C0123456789)")
|
||||
bot_user_id: Optional[str] = Field(None, description="Bot user ID (will be auto-detected)")
|
||||
|
||||
|
||||
class AIConfig(BaseModel):
|
||||
"""AI model configuration"""
|
||||
|
||||
@@ -58,8 +67,9 @@ class GovernanceConfig(BaseModel):
|
||||
class PlatformConfig(BaseModel):
|
||||
"""Platform selection and configuration"""
|
||||
|
||||
type: str = Field(..., description="Platform type: mastodon, discord, telegram, mock")
|
||||
type: str = Field(..., description="Platform type: mastodon, slack, discord, telegram, mock")
|
||||
mastodon: Optional[MastodonConfig] = Field(None, description="Mastodon configuration")
|
||||
slack: Optional[SlackConfig] = Field(None, description="Slack configuration")
|
||||
# Future platforms:
|
||||
# discord: Optional[DiscordConfig] = None
|
||||
# telegram: Optional[TelegramConfig] = None
|
||||
@@ -117,7 +127,7 @@ def create_example_config(output_path: str = "config/config.example.yaml"):
|
||||
"""
|
||||
example_config = {
|
||||
"platform": {
|
||||
"type": "mastodon", # or "discord", "telegram", "mock"
|
||||
"type": "mastodon", # or "slack", "discord", "telegram", "mock"
|
||||
"mastodon": {
|
||||
"instance_url": "https://your-mastodon-instance.social",
|
||||
"client_id": "your_client_id_here",
|
||||
@@ -125,6 +135,12 @@ def create_example_config(output_path: str = "config/config.example.yaml"):
|
||||
"access_token": "your_access_token_here",
|
||||
"bot_username": "govbot",
|
||||
},
|
||||
# Slack example:
|
||||
# "slack": {
|
||||
# "bot_token": "xoxb-your-bot-token-here",
|
||||
# "app_token": "xapp-your-app-token-here",
|
||||
# "channel_id": "C0123456789",
|
||||
# },
|
||||
# Discord example (for future use):
|
||||
# "discord": {
|
||||
# "token": "your_discord_bot_token",
|
||||
|
||||
Reference in New Issue
Block a user