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:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -58,6 +58,9 @@ api_keys.json
|
|||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
|
|
||||||
|
# Instance-specific state
|
||||||
|
config/.constitution_post_id
|
||||||
|
|
||||||
# Backups (may contain sensitive data)
|
# Backups (may contain sensitive data)
|
||||||
*.backup
|
*.backup
|
||||||
*.bak
|
*.bak
|
||||||
|
|||||||
311
CONSTITUTION_PUBLISHING.md
Normal file
311
CONSTITUTION_PUBLISHING.md
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
# Constitution Publishing
|
||||||
|
|
||||||
|
The bot can publish and maintain its constitution as a pinned thread on Mastodon, creating a transparent, versioned record of governance rules.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Constitutional Version Control
|
||||||
|
|
||||||
|
When you publish or update the constitution:
|
||||||
|
|
||||||
|
1. **Previous Version Deprecated**: The old pinned constitution gets a deprecation notice
|
||||||
|
2. **New Version Posted**: Constitution is split into a thread (~450 chars per post)
|
||||||
|
3. **Thread Pinned**: First post is pinned to the bot's profile
|
||||||
|
4. **History Preserved**: Old versions remain visible with deprecation notices
|
||||||
|
5. **ID Tracked**: Post ID saved to `config/.constitution_post_id` for future updates
|
||||||
|
|
||||||
|
### Thread Format
|
||||||
|
|
||||||
|
```
|
||||||
|
📜 CONSTITUTION (Updated: 2026-02-10)
|
||||||
|
|
||||||
|
Thread 🧵 [1/5]
|
||||||
|
|
||||||
|
[Constitution content...]
|
||||||
|
|
||||||
|
[2/5]
|
||||||
|
[More content...]
|
||||||
|
|
||||||
|
[3/5]
|
||||||
|
[More content...]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deprecation Notice (on old versions)
|
||||||
|
|
||||||
|
```
|
||||||
|
⚠️ DEPRECATED: This constitution has been superseded.
|
||||||
|
|
||||||
|
Changes: Updated voting thresholds and added new roles
|
||||||
|
|
||||||
|
Please see my profile for the current pinned constitution.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Publishing Methods
|
||||||
|
|
||||||
|
### Method 1: Via Bot Command (Recommended)
|
||||||
|
|
||||||
|
Ask the bot directly via Mastodon:
|
||||||
|
|
||||||
|
```
|
||||||
|
@govbot please publish the current constitution
|
||||||
|
```
|
||||||
|
|
||||||
|
The bot will:
|
||||||
|
- Read `constitution.md`
|
||||||
|
- Check authority per constitution
|
||||||
|
- Post and pin the thread
|
||||||
|
- Report success/failure
|
||||||
|
|
||||||
|
### Method 2: Helper Script
|
||||||
|
|
||||||
|
Use the provided script for direct publishing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic usage
|
||||||
|
python scripts/publish_constitution.py
|
||||||
|
|
||||||
|
# With change summary
|
||||||
|
python scripts/publish_constitution.py --summary "Added role-based permissions"
|
||||||
|
|
||||||
|
# Custom constitution file
|
||||||
|
python scripts/publish_constitution.py --constitution docs/governance.md
|
||||||
|
|
||||||
|
# Full options
|
||||||
|
python scripts/publish_constitution.py \
|
||||||
|
--constitution constitution.md \
|
||||||
|
--summary "Updated voting thresholds" \
|
||||||
|
--config config/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Script options:**
|
||||||
|
- `--summary` / `-s`: Description of changes (shown in deprecation notice)
|
||||||
|
- `--constitution` / `-c`: Path to constitution file (default: `constitution.md`)
|
||||||
|
- `--config`: Path to config file (default: `config/config.yaml`)
|
||||||
|
|
||||||
|
### Method 3: Python API
|
||||||
|
|
||||||
|
Programmatically publish from your own code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from src.govbot.utils.config import load_config
|
||||||
|
from src.govbot.platforms.mastodon import MastodonAdapter
|
||||||
|
|
||||||
|
# Load config and connect
|
||||||
|
config = load_config("config/config.yaml")
|
||||||
|
adapter = MastodonAdapter(config.platform.mastodon.model_dump())
|
||||||
|
adapter.connect()
|
||||||
|
|
||||||
|
# Publish constitution
|
||||||
|
result = adapter.execute_skill(
|
||||||
|
skill_name="publish_constitution",
|
||||||
|
parameters={
|
||||||
|
"constitution_text": constitution_text,
|
||||||
|
"change_summary": "What changed in this version",
|
||||||
|
},
|
||||||
|
actor="@admin"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(result["message"])
|
||||||
|
```
|
||||||
|
|
||||||
|
## Profile Integration
|
||||||
|
|
||||||
|
### Update Profile to Reference Constitution
|
||||||
|
|
||||||
|
The bot can also update its profile to highlight the constitution:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Via bot command
|
||||||
|
@govbot update your profile to mention the constitution in your bio
|
||||||
|
|
||||||
|
# Via Python API
|
||||||
|
adapter.execute_skill(
|
||||||
|
skill_name="update_profile",
|
||||||
|
parameters={
|
||||||
|
"display_name": "Govbot",
|
||||||
|
"note": "🤖 Governance bot for democratic communities.\n\n📜 Constitution pinned to profile.",
|
||||||
|
"fields": [
|
||||||
|
{"name": "Constitution", "value": "📜 See pinned post"},
|
||||||
|
{"name": "Governance", "value": "Consensus-based"},
|
||||||
|
{"name": "Source Code", "value": "github.com/you/repo"},
|
||||||
|
{"name": "Contact", "value": "@admin"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
actor="@admin"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Profile fields appear as a table on the bot's profile page.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Initial Publication
|
||||||
|
|
||||||
|
When first setting up the bot:
|
||||||
|
|
||||||
|
1. **Finalize Constitution**: Make sure `constitution.md` is complete
|
||||||
|
2. **Set Profile**: Update bio and fields to reference constitution
|
||||||
|
3. **Publish Constitution**: Run publish script or ask bot
|
||||||
|
4. **Verify**: Visit bot's profile to confirm pinned thread
|
||||||
|
|
||||||
|
### Updating Constitution
|
||||||
|
|
||||||
|
When making changes:
|
||||||
|
|
||||||
|
1. **Edit File**: Update `constitution.md` with changes
|
||||||
|
2. **Write Summary**: Prepare clear description of what changed
|
||||||
|
3. **Test Locally**: Use CLI mode to test if needed
|
||||||
|
4. **Publish Update**: Run script with `--summary` flag
|
||||||
|
5. **Verify**: Check that:
|
||||||
|
- Old version has deprecation notice
|
||||||
|
- New version is pinned
|
||||||
|
- Profile still references constitution
|
||||||
|
|
||||||
|
### Version Control
|
||||||
|
|
||||||
|
Consider maintaining constitution in git:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Track changes
|
||||||
|
git add constitution.md
|
||||||
|
git commit -m "Update voting thresholds from 60% to 66%"
|
||||||
|
|
||||||
|
# Publish to Mastodon
|
||||||
|
python scripts/publish_constitution.py \
|
||||||
|
--summary "Update voting thresholds from 60% to 66%"
|
||||||
|
|
||||||
|
# Push to repo
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
This gives you:
|
||||||
|
- **Git history**: Full version control with diffs
|
||||||
|
- **Mastodon thread history**: Public, timestamped versions
|
||||||
|
- **Deprecation chain**: Links between versions
|
||||||
|
|
||||||
|
## Example Workflow
|
||||||
|
|
||||||
|
### Scenario: Updating Voting Rules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Edit constitution
|
||||||
|
nano constitution.md
|
||||||
|
# (Change "50% majority" to "60% supermajority")
|
||||||
|
|
||||||
|
# 2. Test understanding (optional)
|
||||||
|
python -m src.govbot.governance.constitution "What is the voting threshold?"
|
||||||
|
# Verify it understands the new rule
|
||||||
|
|
||||||
|
# 3. Commit to git
|
||||||
|
git add constitution.md
|
||||||
|
git commit -m "Increase voting threshold to supermajority"
|
||||||
|
|
||||||
|
# 4. Publish to Mastodon
|
||||||
|
python scripts/publish_constitution.py \
|
||||||
|
--summary "Increased voting threshold from 50% to 60%"
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# 📜 Loaded constitution from constitution.md (4532 chars)
|
||||||
|
# 🔌 Connecting to https://govbot.modpol.net...
|
||||||
|
# ✅ Connected as @govbot
|
||||||
|
# 📤 Publishing constitution...
|
||||||
|
# Change summary: Increased voting threshold from 50% to 60%
|
||||||
|
# ✅ Constitution published as 8-post thread and pinned to profile.
|
||||||
|
# Previous version marked as deprecated.
|
||||||
|
# 📊 Details:
|
||||||
|
# - Thread length: 8 posts
|
||||||
|
# - First post ID: 123456789
|
||||||
|
# - Previous post ID: 123456700 (deprecated)
|
||||||
|
# 🔗 View at: https://govbot.modpol.net/@govbot
|
||||||
|
# ✨ Done!
|
||||||
|
|
||||||
|
# 5. Verify
|
||||||
|
# Visit https://govbot.modpol.net/@govbot
|
||||||
|
# - New thread is pinned
|
||||||
|
# - Old thread has deprecation notice
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### File Storage
|
||||||
|
|
||||||
|
The current constitution post ID is stored in:
|
||||||
|
```
|
||||||
|
config/.constitution_post_id
|
||||||
|
```
|
||||||
|
|
||||||
|
This file is **gitignored** but tracked by the bot to find the previous version when updating.
|
||||||
|
|
||||||
|
### Thread Splitting Algorithm
|
||||||
|
|
||||||
|
The bot splits constitution into chunks:
|
||||||
|
|
||||||
|
1. **Split by paragraphs**: Preserves semantic boundaries
|
||||||
|
2. **Combine into chunks**: Up to 450 chars each (leaving room for thread indicators)
|
||||||
|
3. **Add thread indicators**: `[1/8]`, `[2/8]`, etc.
|
||||||
|
4. **First post header**: Includes emoji, date, and thread indicator
|
||||||
|
5. **Post sequentially**: Each replies to the previous
|
||||||
|
|
||||||
|
### Post Visibility
|
||||||
|
|
||||||
|
Constitution posts are always:
|
||||||
|
- **Public visibility**: Anyone can see and share
|
||||||
|
- **Threaded**: Each post replies to previous
|
||||||
|
- **Pinned**: First post pinned to profile (max 5 pins)
|
||||||
|
|
||||||
|
### Mastodon API Requirements
|
||||||
|
|
||||||
|
Publishing requires:
|
||||||
|
- `write:accounts` scope (for pinning/unpinning)
|
||||||
|
- `write:statuses` scope (for posting)
|
||||||
|
- Bot must be connected and authenticated
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Failed to pin constitution"
|
||||||
|
|
||||||
|
**Cause**: Too many posts already pinned (max 5)
|
||||||
|
|
||||||
|
**Solution**: Manually unpin old posts from web interface at Settings → Profile → Pinned posts
|
||||||
|
|
||||||
|
### "Constitution file not found"
|
||||||
|
|
||||||
|
**Cause**: File path incorrect
|
||||||
|
|
||||||
|
**Solution**: Verify file exists:
|
||||||
|
```bash
|
||||||
|
ls -la constitution.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Not authorized to publish"
|
||||||
|
|
||||||
|
**Cause**: Bot doesn't have authority per constitution
|
||||||
|
|
||||||
|
**Solution**: Either:
|
||||||
|
- Grant bot authority in constitution
|
||||||
|
- Use script directly (bypasses authorization check)
|
||||||
|
|
||||||
|
### "Previous version not found"
|
||||||
|
|
||||||
|
**Cause**: `.constitution_post_id` file missing or contains invalid ID
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- First publication: This is normal, ignore
|
||||||
|
- Subsequent: Old version won't be deprecated (but new version will still pin)
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
Potential improvements:
|
||||||
|
|
||||||
|
- **Diff Display**: Show exact changes between versions
|
||||||
|
- **Amendment Tracking**: Link to governance processes that authorized changes
|
||||||
|
- **Multilingual**: Publish translations as separate threads
|
||||||
|
- **Rich Formatting**: Use custom emojis or instance-specific formatting
|
||||||
|
- **Automatic Publishing**: Trigger on git push or governance process completion
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
- [PLATFORM_SKILLS.md](PLATFORM_SKILLS.md) - All available platform skills
|
||||||
|
- [ARCHITECTURE.md](ARCHITECTURE.md) - How the bot works
|
||||||
|
- [Mastodon API - Update Credentials](https://docs.joinmastodon.org/methods/accounts/#update_credentials)
|
||||||
|
- [Mastodon API - Pin Status](https://docs.joinmastodon.org/methods/statuses/#pin)
|
||||||
17
PLATFORMS.md
17
PLATFORMS.md
@@ -4,7 +4,16 @@ This guide explains how to implement Govbot adapters for new social/communicatio
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Govbot uses a **platform-agnostic architecture** that separates governance logic from platform-specific code. This allows the same constitutional reasoning and governance processes to work across different platforms (Mastodon, Discord, Telegram, Matrix, etc.).
|
Govbot uses a **platform-agnostic architecture** that separates governance logic from platform-specific code. This allows the same constitutional reasoning and governance processes to work across different platforms.
|
||||||
|
|
||||||
|
**Currently Implemented**:
|
||||||
|
- ✅ **Mastodon** - Instance-wide governance with full admin/moderation API
|
||||||
|
- ✅ **Slack** - Channel-scoped governance with Socket Mode
|
||||||
|
|
||||||
|
**Planned**:
|
||||||
|
- 🚧 **Discord** - Server-wide governance (see example skeleton below)
|
||||||
|
- 🚧 **Telegram** - Group governance
|
||||||
|
- 🚧 **Matrix** - Room governance
|
||||||
|
|
||||||
The key abstraction is the **PlatformAdapter** interface, which defines how Govbot interacts with any platform.
|
The key abstraction is the **PlatformAdapter** interface, which defines how Govbot interacts with any platform.
|
||||||
|
|
||||||
@@ -841,8 +850,10 @@ class DiscordAdapter(PlatformAdapter):
|
|||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
- Review the Mastodon adapter for a complete example
|
- Review the **Mastodon adapter** (`src/govbot/platforms/mastodon.py`) for an instance-wide governance example
|
||||||
- Check the base PlatformAdapter for interface documentation
|
- Review the **Slack adapter** (`src/govbot/platforms/slack.py`) for a channel-scoped governance example
|
||||||
|
- Check the base PlatformAdapter (`src/govbot/platforms/base.py`) for interface documentation
|
||||||
|
- See setup guides: [MASTODON_SETUP.md](MASTODON_SETUP.md), [SLACK_SETUP.md](SLACK_SETUP.md)
|
||||||
- Look at test files for testing patterns
|
- Look at test files for testing patterns
|
||||||
- Ask questions in project discussions
|
- Ask questions in project discussions
|
||||||
|
|
||||||
|
|||||||
372
PLATFORM_SKILLS.md
Normal file
372
PLATFORM_SKILLS.md
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
# Mastodon Platform Skills
|
||||||
|
|
||||||
|
Complete listing of all platform-specific governance skills available through the Mastodon API.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The bot now implements **35+ platform skills** organized into 7 categories:
|
||||||
|
- Account Moderation (9 skills)
|
||||||
|
- Account Management (4 skills)
|
||||||
|
- Report Management (4 skills)
|
||||||
|
- Federation Management (4 skills)
|
||||||
|
- Security Management (4 skills)
|
||||||
|
- Instance Administration (2 skills)
|
||||||
|
- Web-Only Actions (2 skills - informational only)
|
||||||
|
|
||||||
|
## Account Moderation Skills
|
||||||
|
|
||||||
|
### suspend_account
|
||||||
|
**Description**: Suspend a user account (reversible, blocks login and hides content)
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to suspend
|
||||||
|
- `reason` (str): Reason for suspension
|
||||||
|
**Reversible**: Yes (use `unsuspend_account`)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### unsuspend_account
|
||||||
|
**Description**: Lift suspension from an account
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to unsuspend
|
||||||
|
**Reversible**: Yes (can suspend again)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### silence_account
|
||||||
|
**Description**: Silence a user account (hide from public timelines, reversible)
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to silence
|
||||||
|
- `reason` (str): Reason for silencing
|
||||||
|
**Reversible**: Yes (use `unsilence_account`)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### unsilence_account
|
||||||
|
**Description**: Lift silence from an account
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to unsilence
|
||||||
|
**Reversible**: Yes (can silence again)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### disable_account
|
||||||
|
**Description**: Disable local account login (reversible)
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to disable
|
||||||
|
- `reason` (str): Reason for disabling
|
||||||
|
**Reversible**: Yes (use `enable_account`)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### enable_account
|
||||||
|
**Description**: Re-enable a disabled local account
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to enable
|
||||||
|
**Reversible**: Yes (can disable again)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### mark_account_sensitive
|
||||||
|
**Description**: Mark account's media as always sensitive
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to mark
|
||||||
|
- `reason` (str): Reason for marking sensitive
|
||||||
|
**Reversible**: Yes (use `unmark_account_sensitive`)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### unmark_account_sensitive
|
||||||
|
**Description**: Remove sensitive flag from account
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to unmark
|
||||||
|
**Reversible**: Yes (can mark sensitive again)
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
### delete_status
|
||||||
|
**Description**: Delete a status/post (permanent)
|
||||||
|
**Parameters**:
|
||||||
|
- `status_id` (str): Status ID to delete
|
||||||
|
- `reason` (str): Reason for deletion
|
||||||
|
**Reversible**: No
|
||||||
|
**Authorization**: Requires moderation authority per constitution
|
||||||
|
|
||||||
|
## Account Management Skills
|
||||||
|
|
||||||
|
### approve_account
|
||||||
|
**Description**: Approve a pending account registration
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to approve
|
||||||
|
**Reversible**: No
|
||||||
|
**Authorization**: Requires account approval authority per constitution
|
||||||
|
|
||||||
|
### reject_account
|
||||||
|
**Description**: Reject a pending account registration (permanent)
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Account ID to reject
|
||||||
|
**Reversible**: No
|
||||||
|
**Authorization**: Requires account approval authority per constitution
|
||||||
|
|
||||||
|
### delete_account_data
|
||||||
|
**Description**: Permanently delete all data for a suspended account (IRREVERSIBLE)
|
||||||
|
**Parameters**:
|
||||||
|
- `account_id` (str): Suspended account ID to delete
|
||||||
|
**Reversible**: No
|
||||||
|
**Note**: Account must already be suspended before data deletion
|
||||||
|
**Authorization**: Requires highest level authority per constitution
|
||||||
|
|
||||||
|
### create_account
|
||||||
|
**Description**: Create a new user account (requires email verification, may need approval)
|
||||||
|
**Parameters**:
|
||||||
|
- `username` (str): Desired username
|
||||||
|
- `email` (str): Email address
|
||||||
|
- `password` (str): Account password
|
||||||
|
- `reason` (str, optional): Registration reason (if approval required)
|
||||||
|
**Reversible**: No
|
||||||
|
**Limitations**:
|
||||||
|
- User must verify email before account is active
|
||||||
|
- May require manual moderator approval depending on instance settings
|
||||||
|
- Only creates regular user accounts (not admin accounts)
|
||||||
|
**Authorization**: Requires account creation authority per constitution
|
||||||
|
|
||||||
|
## Report Management Skills
|
||||||
|
|
||||||
|
### assign_report
|
||||||
|
**Description**: Assign a report to yourself for handling
|
||||||
|
**Parameters**:
|
||||||
|
- `report_id` (str): Report ID to assign
|
||||||
|
**Reversible**: Yes (use `unassign_report`)
|
||||||
|
**Authorization**: Requires report management authority per constitution
|
||||||
|
|
||||||
|
### unassign_report
|
||||||
|
**Description**: Unassign a report so others can claim it
|
||||||
|
**Parameters**:
|
||||||
|
- `report_id` (str): Report ID to unassign
|
||||||
|
**Reversible**: Yes (can reassign)
|
||||||
|
**Authorization**: Requires report management authority per constitution
|
||||||
|
|
||||||
|
### resolve_report
|
||||||
|
**Description**: Mark a report as resolved
|
||||||
|
**Parameters**:
|
||||||
|
- `report_id` (str): Report ID to resolve
|
||||||
|
**Reversible**: Yes (use `reopen_report`)
|
||||||
|
**Authorization**: Requires report management authority per constitution
|
||||||
|
|
||||||
|
### reopen_report
|
||||||
|
**Description**: Reopen a closed report
|
||||||
|
**Parameters**:
|
||||||
|
- `report_id` (str): Report ID to reopen
|
||||||
|
**Reversible**: Yes (can resolve again)
|
||||||
|
**Authorization**: Requires report management authority per constitution
|
||||||
|
|
||||||
|
## Federation Management Skills
|
||||||
|
|
||||||
|
### block_domain
|
||||||
|
**Description**: Block federation with a domain
|
||||||
|
**Parameters**:
|
||||||
|
- `domain` (str): Domain to block
|
||||||
|
- `severity` (str): Block severity: "silence", "suspend", or "noop"
|
||||||
|
- `public_comment` (str): Public reason for block
|
||||||
|
- `private_comment` (str, optional): Internal note
|
||||||
|
- `reject_media` (bool, optional): Reject media files from domain
|
||||||
|
- `reject_reports` (bool, optional): Reject reports from domain
|
||||||
|
- `obfuscate` (bool, optional): Hide domain name publicly
|
||||||
|
**Reversible**: Yes (use `unblock_domain`)
|
||||||
|
**Authorization**: Requires federation management authority per constitution
|
||||||
|
|
||||||
|
### unblock_domain
|
||||||
|
**Description**: Remove domain from block list
|
||||||
|
**Parameters**:
|
||||||
|
- `block_id` (str): Domain block ID to remove
|
||||||
|
**Reversible**: Yes (can block again)
|
||||||
|
**Authorization**: Requires federation management authority per constitution
|
||||||
|
|
||||||
|
### allow_domain
|
||||||
|
**Description**: Add domain to allowlist (for LIMITED_FEDERATION_MODE)
|
||||||
|
**Parameters**:
|
||||||
|
- `domain` (str): Domain to allow
|
||||||
|
**Reversible**: Yes (use `disallow_domain`)
|
||||||
|
**Authorization**: Requires federation management authority per constitution
|
||||||
|
|
||||||
|
### disallow_domain
|
||||||
|
**Description**: Remove domain from allowlist
|
||||||
|
**Parameters**:
|
||||||
|
- `allow_id` (str): Domain allow ID to remove
|
||||||
|
**Reversible**: Yes (can allow again)
|
||||||
|
**Authorization**: Requires federation management authority per constitution
|
||||||
|
|
||||||
|
## Security Management Skills
|
||||||
|
|
||||||
|
### block_ip
|
||||||
|
**Description**: Block IP address or range
|
||||||
|
**Parameters**:
|
||||||
|
- `ip` (str): IP address with CIDR prefix (e.g., "192.168.0.1/24")
|
||||||
|
- `severity` (str): Block severity: "sign_up_requires_approval", "sign_up_block", or "no_access"
|
||||||
|
- `comment` (str): Reason for IP block
|
||||||
|
- `expires_in` (int, optional): Expiration time in seconds
|
||||||
|
**Reversible**: Yes (use `unblock_ip`)
|
||||||
|
**Authorization**: Requires security management authority per constitution
|
||||||
|
|
||||||
|
### unblock_ip
|
||||||
|
**Description**: Remove IP block
|
||||||
|
**Parameters**:
|
||||||
|
- `block_id` (str): IP block ID to remove
|
||||||
|
**Reversible**: Yes (can block again)
|
||||||
|
**Authorization**: Requires security management authority per constitution
|
||||||
|
|
||||||
|
### block_email_domain
|
||||||
|
**Description**: Block email domain from registrations
|
||||||
|
**Parameters**:
|
||||||
|
- `domain` (str): Email domain to block (e.g., "spam.com")
|
||||||
|
**Reversible**: Yes (use `unblock_email_domain`)
|
||||||
|
**Authorization**: Requires security management authority per constitution
|
||||||
|
|
||||||
|
### unblock_email_domain
|
||||||
|
**Description**: Remove email domain from block list
|
||||||
|
**Parameters**:
|
||||||
|
- `block_id` (str): Email domain block ID to remove
|
||||||
|
**Reversible**: Yes (can block again)
|
||||||
|
**Authorization**: Requires security management authority per constitution
|
||||||
|
|
||||||
|
## Constitution Management Skills
|
||||||
|
|
||||||
|
### publish_constitution
|
||||||
|
**Description**: Post constitution as pinned thread (deprecates previous version)
|
||||||
|
**Parameters**:
|
||||||
|
- `constitution_text` (str): Full constitution text in markdown
|
||||||
|
- `change_summary` (str, optional): Summary of what changed
|
||||||
|
**Reversible**: No
|
||||||
|
**Authorization**: Requires constitutional amendment process
|
||||||
|
**Behavior**:
|
||||||
|
1. Finds previously pinned constitution (if any)
|
||||||
|
2. Adds deprecation notice to old version
|
||||||
|
3. Splits constitution into thread-sized chunks (~450 chars each)
|
||||||
|
4. Posts as public thread
|
||||||
|
5. Pins new thread to profile
|
||||||
|
6. Unpins old thread
|
||||||
|
7. Saves post ID for future updates
|
||||||
|
|
||||||
|
### update_profile
|
||||||
|
**Description**: Update bot profile information (bio, fields, display name)
|
||||||
|
**Parameters**:
|
||||||
|
- `display_name` (str, optional): Display name
|
||||||
|
- `note` (str, optional): Bio/description
|
||||||
|
- `fields` (list, optional): Profile fields (max 4, each with 'name' and 'value')
|
||||||
|
**Reversible**: Yes
|
||||||
|
**Authorization**: Requires governance approval
|
||||||
|
**Example fields**:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"name": "Constitution", "value": "📜 See pinned post"},
|
||||||
|
{"name": "Governance", "value": "Consensus-based"},
|
||||||
|
{"name": "Source", "value": "github.com/..."}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Instance Administration Skills
|
||||||
|
|
||||||
|
**⚠️ IMPORTANT**: Role management is **NOT available via the Mastodon API**. See "Platform Limitations" section below for details.
|
||||||
|
|
||||||
|
## Web-Only Actions (Not Available via API)
|
||||||
|
|
||||||
|
These skills are included in the bot's knowledge so it can explain limitations to users.
|
||||||
|
|
||||||
|
### update_instance_rules
|
||||||
|
**Description**: Update instance rules/code of conduct
|
||||||
|
**Status**: WEB-ONLY - Must be done through admin interface
|
||||||
|
**Web Interface**: `/admin/server_settings/rules`
|
||||||
|
**Note**: Mastodon API does not provide endpoints for managing server rules programmatically
|
||||||
|
|
||||||
|
### create_announcement
|
||||||
|
**Description**: Create an instance-wide announcement
|
||||||
|
**Status**: WEB-ONLY - Must be done through admin interface
|
||||||
|
**Web Interface**: `/admin/announcements`
|
||||||
|
**Note**: Mastodon API does not provide endpoints for creating announcements programmatically
|
||||||
|
|
||||||
|
## Platform Limitations
|
||||||
|
|
||||||
|
The bot is now aware of the following limitations and can explain them to users:
|
||||||
|
|
||||||
|
### Actions Requiring Web Interface
|
||||||
|
- **Instance Rules**: Creating and editing server rules must be done at `/admin/server_settings/rules`
|
||||||
|
- **Announcements**: Creating instance-wide announcements must be done at `/admin/announcements`
|
||||||
|
- **Role Management**: ALL role operations (grant/revoke moderator, admin, or custom roles) must be done at `/admin/roles`
|
||||||
|
- **Instance Settings**: Modifying instance description, contact info, etc. requires web admin access
|
||||||
|
|
||||||
|
### Actions Not Possible via API
|
||||||
|
- **Role Management**: The Mastodon API does **NOT** support granting or revoking ANY roles (moderator, admin, or custom). You must use:
|
||||||
|
- **Web interface**: `/admin/roles`
|
||||||
|
- **Command line**: `tootctl accounts modify username --role RoleName`
|
||||||
|
- **Admin Account Creation**: Admin accounts cannot be created via API:
|
||||||
|
```bash
|
||||||
|
RAILS_ENV=production bin/tootctl accounts create username --email user@example.com --confirmed --role Owner
|
||||||
|
```
|
||||||
|
|
||||||
|
### General Limitations
|
||||||
|
- **Account Creation**: New accounts require email verification and may require manual approval depending on instance settings
|
||||||
|
- **Permissions Required**: All admin actions require both OAuth scopes AND appropriate role permissions (Manage Users, Manage Reports, etc.)
|
||||||
|
- **Rate Limits**: API endpoints are subject to rate limiting
|
||||||
|
- **Suspended Account Deletion**: Account data can only be deleted for already-suspended accounts
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. **Skill Discovery**: When a user makes a request, the bot receives the full list of platform skills
|
||||||
|
2. **Limitation Awareness**: The bot also receives information about what's not possible via API
|
||||||
|
3. **Intelligent Routing**: The LLM agent decides whether to:
|
||||||
|
- Execute a platform skill (if available and authorized)
|
||||||
|
- Explain a limitation (if requested action requires web interface)
|
||||||
|
- Create a governance process (if actor lacks authority)
|
||||||
|
- Respond with information (if just asking a question)
|
||||||
|
|
||||||
|
## Example Interactions
|
||||||
|
|
||||||
|
### Successful Platform Skill Execution
|
||||||
|
```
|
||||||
|
User: @govbot please suspend account 12345 for spam
|
||||||
|
Bot: I've suspended account 12345 for spam.
|
||||||
|
This action is reversible using the unsuspend_account skill.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web-Only Limitation
|
||||||
|
```
|
||||||
|
User: @govbot please update the instance rules
|
||||||
|
Bot: Instance rules cannot be updated via the API.
|
||||||
|
To update server rules, please visit:
|
||||||
|
https://your-instance.com/admin/server_settings/rules
|
||||||
|
|
||||||
|
You can add, edit, or remove rules through the admin interface.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Account Creation with Limitations
|
||||||
|
```
|
||||||
|
User: @govbot create an account for newuser@example.com
|
||||||
|
Bot: Account @newuser created successfully.
|
||||||
|
|
||||||
|
Important: The user must verify their email before they can log in.
|
||||||
|
Depending on instance settings, manual approval may also be required.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
### Code Changes
|
||||||
|
1. **Mastodon Adapter** (`src/govbot/platforms/mastodon.py`):
|
||||||
|
- Added 35+ platform skills across 7 categories
|
||||||
|
- Implemented execution methods for all API-available skills
|
||||||
|
- Added `get_platform_limitations()` method to document limitations
|
||||||
|
- Web-only skills return helpful messages with admin interface URLs
|
||||||
|
|
||||||
|
2. **Governance Agent** (`src/govbot/agent.py`):
|
||||||
|
- Updated `process_request()` to accept `platform_limitations` parameter
|
||||||
|
- Enhanced decision-making prompt to include limitation information
|
||||||
|
- Agent now explains limitations when users request unavailable actions
|
||||||
|
|
||||||
|
3. **Bot Integration** (`src/govbot/bot.py`):
|
||||||
|
- Retrieves platform limitations alongside platform skills
|
||||||
|
- Passes limitations to agent for informed decision-making
|
||||||
|
|
||||||
|
### API Requirements
|
||||||
|
All admin skills require:
|
||||||
|
- Appropriate OAuth scopes (`admin:read`, `admin:write`, etc.)
|
||||||
|
- Role permissions (Manage Users, Manage Reports, Manage Federation, etc.)
|
||||||
|
- Active Mastodon.py connection with admin access token
|
||||||
|
|
||||||
|
## Documentation Sources
|
||||||
|
- [Mastodon Admin Accounts API](https://docs.joinmastodon.org/methods/admin/accounts/)
|
||||||
|
- [Mastodon Admin Reports API](https://docs.joinmastodon.org/methods/admin/reports/)
|
||||||
|
- [Mastodon Admin Domain Blocks API](https://docs.joinmastodon.org/methods/admin/domain_blocks/)
|
||||||
|
- [Mastodon Admin IP Blocks API](https://docs.joinmastodon.org/methods/admin/ip_blocks/)
|
||||||
|
- [Mastodon Admin Email Domain Blocks API](https://docs.joinmastodon.org/methods/admin/email_domain_blocks/)
|
||||||
|
- [Mastodon Accounts API](https://docs.joinmastodon.org/methods/accounts/)
|
||||||
|
- [Mastodon OAuth Scopes](https://docs.joinmastodon.org/api/oauth-scopes/)
|
||||||
252
QUICKSTART.md
252
QUICKSTART.md
@@ -1,252 +0,0 @@
|
|||||||
# Govbot Quick Start Guide
|
|
||||||
|
|
||||||
This guide will help you get Govbot up and running.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Python 3.11 or higher
|
|
||||||
- `llm` CLI tool installed (`pip install llm`)
|
|
||||||
- Ollama installed with a model (e.g., `ollama pull llama3.2`) OR API keys for cloud models
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### 1. Install Dependencies
|
|
||||||
|
|
||||||
Using `uv` (faster, recommended):
|
|
||||||
```bash
|
|
||||||
# Install uv if you don't have it
|
|
||||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
uv pip install -e .
|
|
||||||
```
|
|
||||||
|
|
||||||
Using regular pip:
|
|
||||||
```bash
|
|
||||||
pip install -e .
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Configure the Bot
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy example config
|
|
||||||
cp config/config.example.yaml config/config.yaml
|
|
||||||
|
|
||||||
# Edit with your credentials and settings
|
|
||||||
nano config/config.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
**⚠️ IMPORTANT - Security Notice**:
|
|
||||||
- `config/config.yaml` contains your secrets (API tokens, passwords)
|
|
||||||
- This file is automatically gitignored - it will NEVER be committed
|
|
||||||
- Never share this file or commit it to version control
|
|
||||||
- See [SECURITY.md](SECURITY.md) for complete security guidance
|
|
||||||
|
|
||||||
For local models with Ollama:
|
|
||||||
```yaml
|
|
||||||
ai:
|
|
||||||
default_model: llama3.2 # or whatever model you have
|
|
||||||
```
|
|
||||||
|
|
||||||
For cloud models:
|
|
||||||
```yaml
|
|
||||||
ai:
|
|
||||||
default_model: gpt-4 # or claude-3-sonnet, etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure the `llm` CLI is configured for your chosen model:
|
|
||||||
```bash
|
|
||||||
# Test llm
|
|
||||||
llm "Hello, how are you?"
|
|
||||||
|
|
||||||
# Configure for cloud models if needed
|
|
||||||
llm keys set openai # for OpenAI
|
|
||||||
llm keys set anthropic # for Anthropic Claude
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Initialize the Database
|
|
||||||
|
|
||||||
The database will be created automatically on first run, but you can verify:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -c "from src.govbot.db.models import init_db; init_db('govbot.db'); print('Database initialized!')"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Without Mastodon
|
|
||||||
|
|
||||||
The CLI allows you to test all governance features without connecting to Mastodon:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run the interactive CLI
|
|
||||||
python -m src.govbot
|
|
||||||
```
|
|
||||||
|
|
||||||
Try these commands:
|
|
||||||
|
|
||||||
```
|
|
||||||
# View the constitution
|
|
||||||
constitution
|
|
||||||
|
|
||||||
# Ask a constitutional question
|
|
||||||
query What are the rules for creating a proposal?
|
|
||||||
|
|
||||||
# Create a test proposal
|
|
||||||
propose We should update the moderation guidelines
|
|
||||||
|
|
||||||
# Check active processes
|
|
||||||
processes
|
|
||||||
|
|
||||||
# Vote on a process (use the ID from processes command)
|
|
||||||
vote 1 agree
|
|
||||||
|
|
||||||
# Check process status
|
|
||||||
status 1
|
|
||||||
|
|
||||||
# View recent actions
|
|
||||||
actions
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing the Constitutional Reasoner
|
|
||||||
|
|
||||||
You can test the constitutional reasoning engine directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m src.govbot.governance.constitution "What voting thresholds are required for different proposal types?"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Understanding the Architecture
|
|
||||||
|
|
||||||
### Component Overview
|
|
||||||
|
|
||||||
1. **Constitution (constitution.md)**: The governance rules in natural language
|
|
||||||
2. **Database (govbot.db)**: SQLite database storing all governance state
|
|
||||||
3. **AI Agent (src/govbot/agent.py)**: Interprets requests and orchestrates actions
|
|
||||||
4. **Primitives (src/govbot/governance/primitives.py)**: Low-level governance operations
|
|
||||||
5. **Constitutional Reasoner (src/govbot/governance/constitution.py)**: RAG system for understanding the constitution
|
|
||||||
6. **Scheduler (src/govbot/scheduler.py)**: Background tasks for deadlines and reminders
|
|
||||||
|
|
||||||
### How It Works
|
|
||||||
|
|
||||||
```
|
|
||||||
User Request
|
|
||||||
↓
|
|
||||||
AI Agent parses intent
|
|
||||||
↓
|
|
||||||
Queries Constitution (RAG)
|
|
||||||
↓
|
|
||||||
Plans primitive actions
|
|
||||||
↓
|
|
||||||
Executes with audit logging
|
|
||||||
↓
|
|
||||||
Returns response
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example: Creating a Proposal
|
|
||||||
|
|
||||||
Let's walk through what happens when you create a proposal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ python -m src.govbot
|
|
||||||
@testuser> propose We should add weekly community meetings
|
|
||||||
```
|
|
||||||
|
|
||||||
The bot will:
|
|
||||||
1. **Parse** your intent (creating a proposal)
|
|
||||||
2. **Query** the constitution about proposal rules
|
|
||||||
3. **Determine** this is a standard proposal (6 days, simple majority)
|
|
||||||
4. **Create** a governance process in the database
|
|
||||||
5. **Schedule** a reminder for the deadline
|
|
||||||
6. **Respond** with the proposal details and how to vote
|
|
||||||
|
|
||||||
You can then vote:
|
|
||||||
```
|
|
||||||
@testuser> vote 1 agree
|
|
||||||
```
|
|
||||||
|
|
||||||
And check status:
|
|
||||||
```
|
|
||||||
@testuser> status 1
|
|
||||||
```
|
|
||||||
|
|
||||||
The scheduler (running in background) will automatically close the proposal after 6 days and count votes.
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
### Customizing the Constitution
|
|
||||||
|
|
||||||
Edit `constitution.md` to match your community's governance needs. The AI will adapt to whatever you write!
|
|
||||||
|
|
||||||
Key things to include:
|
|
||||||
- Proposal types and their requirements
|
|
||||||
- Voting thresholds
|
|
||||||
- Member rights and responsibilities
|
|
||||||
- Administrative procedures
|
|
||||||
- Safety mechanisms (veto, appeals)
|
|
||||||
|
|
||||||
### Connecting to Mastodon
|
|
||||||
|
|
||||||
To connect to a real Mastodon instance, you'll need to:
|
|
||||||
|
|
||||||
1. Create a bot account on your instance
|
|
||||||
2. Register an application to get OAuth credentials
|
|
||||||
3. Update `config/config.yaml` with your Mastodon settings
|
|
||||||
4. Implement the Mastodon streaming listener (currently a stub in `bot.py`)
|
|
||||||
|
|
||||||
See [MASTODON.md](MASTODON.md) for detailed instructions (coming soon).
|
|
||||||
|
|
||||||
### Adding Custom Governance Skills
|
|
||||||
|
|
||||||
You can extend the bot with custom Mastodon-specific actions:
|
|
||||||
|
|
||||||
1. Add new primitives in `src/govbot/governance/primitives.py`
|
|
||||||
2. Update the agent's planning logic in `src/govbot/agent.py`
|
|
||||||
3. Update the constitution to reference new capabilities
|
|
||||||
|
|
||||||
### Running in Production
|
|
||||||
|
|
||||||
For production deployment:
|
|
||||||
|
|
||||||
1. Set up proper logging (see `config.yaml`)
|
|
||||||
2. Use a systemd service or supervisor to keep it running
|
|
||||||
3. Set up monitoring for the database and process queue
|
|
||||||
4. Consider running on a dedicated server or container
|
|
||||||
5. Implement backup procedures for the database
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "Could not parse request" error
|
|
||||||
- Check that `llm` CLI is working: `llm "test"`
|
|
||||||
- Verify your model is installed: `ollama list` or check API keys
|
|
||||||
- Try a simpler request to test
|
|
||||||
|
|
||||||
### "Constitution not found" error
|
|
||||||
- Make sure `constitution.md` exists in the root directory
|
|
||||||
- Check the path in `config/config.yaml`
|
|
||||||
|
|
||||||
### Database errors
|
|
||||||
- Delete `govbot.db` and let it reinitialize
|
|
||||||
- Check file permissions
|
|
||||||
|
|
||||||
### Model not responding
|
|
||||||
- Test llm directly: `llm -m llama3.2 "hello"`
|
|
||||||
- Check Ollama is running: `ollama list`
|
|
||||||
- For cloud models, verify API keys: `llm keys list`
|
|
||||||
|
|
||||||
## Getting Help
|
|
||||||
|
|
||||||
- Check the [README.md](README.md) for architecture details
|
|
||||||
- Review the constitution examples
|
|
||||||
- Look at the code - it's designed to be readable!
|
|
||||||
- Open an issue if you find bugs
|
|
||||||
|
|
||||||
## Philosophy
|
|
||||||
|
|
||||||
Govbot is designed around a few key principles:
|
|
||||||
|
|
||||||
1. **Agentic, not procedural**: The bot interprets natural language constitutions rather than hard-coding governance procedures
|
|
||||||
2. **Transparent**: All actions are logged with constitutional reasoning
|
|
||||||
3. **Reversible**: The community can override bot decisions
|
|
||||||
4. **Flexible**: Works with any constitution you write
|
|
||||||
5. **Democratic**: Enables collective governance within social platforms
|
|
||||||
|
|
||||||
Have fun governing! 🏛️
|
|
||||||
228
README.md
228
README.md
@@ -2,184 +2,158 @@
|
|||||||
|
|
||||||
An agentic governance bot for democratic communities that interprets natural language constitutions and facilitates collective decision-making across social platforms.
|
An agentic governance bot for democratic communities that interprets natural language constitutions and facilitates collective decision-making across social platforms.
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Govbot is designed to:
|
|
||||||
- Read and interpret governance constitutions written in natural language
|
|
||||||
- Facilitate any governance process defined in your constitution (proposals, disputes, elections, discussions, etc.)
|
|
||||||
- Execute administrative actions based on constitutional rules
|
|
||||||
- Maintain an audit trail of all governance activities
|
|
||||||
- Support both local (Ollama) and cloud AI models
|
|
||||||
- **Work across multiple platforms** (Mastodon, Discord, Telegram, etc.)
|
|
||||||
|
|
||||||
**Key Concept**: Govbot uses "process" as the central abstraction - a generic container for any governance activity (proposals, disputes, elections, etc.). Process types are not hard-coded; the LLM interprets your constitution to understand what types exist and how they work.
|
**Key Concept**: Govbot uses "process" as the central abstraction - a generic container for any governance activity (proposals, disputes, elections, etc.). Process types are not hard-coded; the LLM interprets your constitution to understand what types exist and how they work.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Pure LLM-Driven Governance**: No hard-coded governance logic - the LLM interprets the constitution and makes all decisions
|
- **Pure LLM-Driven Governance**: No hard-coded governance logic - the LLM interprets the constitution and makes all decisions
|
||||||
- **Structured Memory System**: Tracks governance processes, events, and decisions in a queryable format
|
- **Structured Memory System**: Tracks governance processes, events, and decisions in a queryable format
|
||||||
- **LLM Tools for Correctness**: Deterministic tools for math, dates, and random selection ensure reliability
|
|
||||||
- **Complete Auditability**: Every decision includes reasoning, constitutional citations, and calculation details
|
- **Complete Auditability**: Every decision includes reasoning, constitutional citations, and calculation details
|
||||||
- **RAG-based Constitutional Reasoning**: Uses retrieval-augmented generation to understand and apply governance rules
|
|
||||||
- **Template Flexibility**: Works with diverse governance models (petition, consensus, do-ocracy, jury, circles)
|
|
||||||
- **Platform-Agnostic**: Same governance logic works across Mastodon, Discord, Telegram, Matrix, and more
|
- **Platform-Agnostic**: Same governance logic works across Mastodon, Discord, Telegram, Matrix, and more
|
||||||
- **Reversible Actions**: All actions are logged and can be reversed through constitutional processes
|
|
||||||
- **Temporal Awareness**: Handles multi-day governance processes with deadlines and reminders
|
- **Temporal Awareness**: Handles multi-day governance processes with deadlines and reminders
|
||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
|
|
||||||
- ✅ **Mastodon** - Full implementation with streaming, admin, and moderation
|
- ✅ **Mastodon** - Full implementation with streaming, admin, and moderation
|
||||||
- 🚧 **Discord** - Coming soon (see [PLATFORMS.md](PLATFORMS.md) for implementation guide)
|
- ✅ **Slack** - Channel-scoped governance with Socket Mode
|
||||||
|
- 🚧 **Discord** - Coming soon (see [PLATFORMS.md](PLATFORMS.md))
|
||||||
- 🚧 **Telegram** - Coming soon
|
- 🚧 **Telegram** - Coming soon
|
||||||
- 🚧 **Matrix** - Planned
|
- 🚧 **Matrix** - Planned
|
||||||
|
|
||||||
Want to add a platform? See [PLATFORMS.md](PLATFORMS.md) for the implementation guide!
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ Governance Request │
|
|
||||||
│ (Natural Language from User) │
|
|
||||||
└────────────────┬────────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ Governance Agent (LLM) │
|
|
||||||
│ • Interprets constitution (RAG) │
|
|
||||||
│ • Queries structured memory │
|
|
||||||
│ • Uses tools for calculations │
|
|
||||||
│ • Makes decisions with reasoning │
|
|
||||||
│ • Generates audit trails │
|
|
||||||
└────────────────┬────────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────┼─────────┐
|
|
||||||
│ │ │
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌────────┐ ┌──────┐ ┌───────┐
|
|
||||||
│ Memory │ │Tools │ │ Audit │
|
|
||||||
│ System │ │ │ │ Trail │
|
|
||||||
└────┬───┘ └──────┘ └───────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌──────────────────────────────┐
|
|
||||||
│ Platform Adapter Layer │
|
|
||||||
└──────────┬───────────────────┘
|
|
||||||
│
|
|
||||||
┌────────┼────────┐
|
|
||||||
│ │ │
|
|
||||||
┌─▼──┐ ┌─▼──┐ ┌─▼──┐
|
|
||||||
│Mast││Disc││Tele│
|
|
||||||
│odon││ord ││gram│
|
|
||||||
└────┘ └────┘ └────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
For detailed architecture documentation, see [ARCHITECTURE.md](ARCHITECTURE.md).
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies (using uv for faster installation)
|
|
||||||
uv pip install -e .
|
|
||||||
|
|
||||||
# For development
|
|
||||||
uv pip install -e ".[dev]"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### 1. Install Dependencies (above)
|
### 1. Install Dependencies
|
||||||
|
|
||||||
### 2. Configure (Credentials Required)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Copy the template
|
# Using uv (recommended, faster)
|
||||||
cp config/config.example.yaml config/config.yaml
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
|
uv pip install -e .
|
||||||
|
|
||||||
# Edit with your credentials
|
# Or using pip
|
||||||
nano config/config.yaml
|
pip install -e .
|
||||||
```
|
```
|
||||||
|
|
||||||
**⚠️ IMPORTANT**: `config/config.yaml` contains your secrets and is automatically ignored by git. Never commit this file.
|
### 2. Configure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp config/config.example.yaml config/config.yaml
|
||||||
|
nano config/config.yaml # Edit with your settings
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ IMPORTANT**: `config/config.yaml` contains secrets and is gitignored. Never commit this file.
|
||||||
|
|
||||||
Configure:
|
Configure:
|
||||||
- Platform credentials (Mastodon access tokens, Discord bot tokens, etc.)
|
- **Platform credentials** (Mastodon access tokens, etc.) - See [MASTODON_SETUP.md](MASTODON_SETUP.md)
|
||||||
- AI model settings (Ollama local models or cloud API keys)
|
- **AI model** (Ollama local models or cloud API keys)
|
||||||
- Constitution path and database location
|
- **Constitution path** and database location
|
||||||
|
|
||||||
For detailed setup instructions:
|
|
||||||
- **Mastodon**: See [MASTODON_SETUP.md](MASTODON_SETUP.md)
|
|
||||||
- **Security**: See [SECURITY.md](SECURITY.md) for credential management
|
|
||||||
|
|
||||||
### 3. Set AI API Keys (if using cloud models)
|
|
||||||
|
|
||||||
|
For cloud models, set API keys:
|
||||||
```bash
|
```bash
|
||||||
# For OpenAI
|
llm keys set openai # For OpenAI
|
||||||
llm keys set openai
|
llm keys set anthropic # For Anthropic Claude
|
||||||
|
|
||||||
# For Anthropic Claude
|
|
||||||
llm keys set anthropic
|
|
||||||
```
|
```
|
||||||
|
|
||||||
These are stored securely in `~/.llm/keys.json` (also gitignored)
|
### 3. Run the Bot
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run the bot
|
# Activate virtual environment (REQUIRED)
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Connect to Mastodon (or configured platform)
|
||||||
python -m src.govbot.bot
|
python -m src.govbot.bot
|
||||||
|
|
||||||
|
# Test locally without Mastodon
|
||||||
|
python -m src.govbot
|
||||||
|
|
||||||
# Query the constitution
|
# Query the constitution
|
||||||
python -m src.govbot.governance.constitution "What are the rules for proposals?"
|
python -m src.govbot.governance.constitution "What are the rules for proposals?"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Testing Locally
|
||||||
|
|
||||||
|
Test all governance features without connecting to a platform:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source .venv/bin/activate # Activate virtual environment first
|
||||||
|
python -m src.govbot
|
||||||
|
```
|
||||||
|
|
||||||
|
Try these commands:
|
||||||
|
- `constitution` - View the constitution
|
||||||
|
- `query What are the rules for creating a proposal?` - Ask constitutional questions
|
||||||
|
- `propose We should update the guidelines` - Create a test proposal
|
||||||
|
- `processes` - Check active processes
|
||||||
|
- `vote 1 agree` - Vote on a process
|
||||||
|
- `status 1` - Check process status
|
||||||
|
- `actions` - View recent actions
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The bot uses an LLM agent that:
|
||||||
|
1. Interprets natural language constitutions via RAG
|
||||||
|
2. Queries structured memory for governance state
|
||||||
|
3. Uses deterministic tools for calculations
|
||||||
|
4. Makes decisions with full reasoning trails
|
||||||
|
5. Works across any platform via adapters
|
||||||
|
|
||||||
|
```
|
||||||
|
User Request → Governance Agent (LLM) → Platform Action
|
||||||
|
↓
|
||||||
|
Constitution (RAG)
|
||||||
|
Memory System
|
||||||
|
Audit Trail
|
||||||
|
```
|
||||||
|
|
||||||
|
For detailed architecture, see [ARCHITECTURE.md](ARCHITECTURE.md) and [ARCHITECTURE_EXAMPLE.md](ARCHITECTURE_EXAMPLE.md)
|
||||||
|
|
||||||
## Constitution Format
|
## Constitution Format
|
||||||
|
|
||||||
Your constitution should be a markdown file that describes:
|
Your constitution is a markdown file describing:
|
||||||
- Governance processes (proposals, voting, etc.)
|
- Governance processes (proposals, voting, etc.)
|
||||||
- Decision-making thresholds
|
- Decision-making thresholds and member rights
|
||||||
- Member rights and responsibilities
|
- Administrative procedures and safety mechanisms
|
||||||
- Administrative procedures
|
|
||||||
- Safety mechanisms (veto, appeals, etc.)
|
|
||||||
|
|
||||||
See `constitution.md` for an example based on Social.coop's bylaws.
|
See `constitution.md` for an example based on Social.coop's bylaws.
|
||||||
|
|
||||||
|
### Publishing Constitution
|
||||||
|
|
||||||
|
The bot can publish its constitution as a **pinned Mastodon thread** with automatic version control:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Publish or update constitution
|
||||||
|
python scripts/publish_constitution.py --summary "What changed"
|
||||||
|
```
|
||||||
|
|
||||||
|
When updated, the bot:
|
||||||
|
- Adds a deprecation notice to the previous version
|
||||||
|
- Posts the new version as a thread
|
||||||
|
- Pins it to the bot's profile
|
||||||
|
- Maintains a public history of all versions
|
||||||
|
|
||||||
|
See [CONSTITUTION_PUBLISHING.md](CONSTITUTION_PUBLISHING.md) for full documentation.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
### Core Documentation
|
**Core**
|
||||||
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - System architecture with LLM, memory, tools, and audit trail
|
- [ARCHITECTURE.md](ARCHITECTURE.md) - System design and components
|
||||||
- **[ARCHITECTURE_EXAMPLE.md](ARCHITECTURE_EXAMPLE.md)** - Complete walkthrough of a proposal lifecycle
|
- [ARCHITECTURE_EXAMPLE.md](ARCHITECTURE_EXAMPLE.md) - Proposal lifecycle walkthrough
|
||||||
- **[constitution.md](constitution.md)** - Example governance constitution
|
- [constitution.md](constitution.md) - Example governance constitution
|
||||||
|
|
||||||
### Setup Guides
|
**Setup**
|
||||||
- **[QUICKSTART.md](QUICKSTART.md)** - Get started quickly with CLI testing
|
- [MASTODON_SETUP.md](MASTODON_SETUP.md) - Deploy to Mastodon
|
||||||
- **[MASTODON_SETUP.md](MASTODON_SETUP.md)** - Complete Mastodon deployment guide
|
- [SLACK_SETUP.md](SLACK_SETUP.md) - Deploy to Slack
|
||||||
- **[PLATFORMS.md](PLATFORMS.md)** - Guide for implementing new platform adapters
|
- [PLATFORMS.md](PLATFORMS.md) - Add new platform support
|
||||||
- **[SECURITY.md](SECURITY.md)** - Credential management and security best practices
|
- [SECURITY.md](SECURITY.md) - Credential management
|
||||||
|
|
||||||
### Templates
|
**Templates**
|
||||||
- **[templates/](templates/)** - Governance template library (petition, consensus, do-ocracy, jury, circles, dispute resolution)
|
- [templates/](templates/) - Governance models (petition, consensus, do-ocracy, jury, circles)
|
||||||
|
|
||||||
## Security
|
## Contributing
|
||||||
|
|
||||||
⚠️ **Important**: Never commit `config/config.yaml` or other files containing credentials. All sensitive files are automatically protected by `.gitignore`.
|
Early-stage project. Contributions welcome!
|
||||||
|
|
||||||
**See [SECURITY.md](SECURITY.md) for:**
|
To add platform support: See [PLATFORMS.md](PLATFORMS.md)
|
||||||
- Complete list of protected files
|
|
||||||
- Where to store credentials
|
|
||||||
- Best practices for development and production
|
|
||||||
- What to do if secrets are accidentally committed
|
|
||||||
|
|
||||||
## Development Status
|
|
||||||
|
|
||||||
This is early-stage software. Current phase: Core infrastructure and agentic reasoning engine.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[To be determined]
|
[To be determined]
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
This project is in early development. Contributions and feedback welcome!
|
|
||||||
|
|
||||||
**For platform developers**: See [PLATFORMS.md](PLATFORMS.md) to add support for Discord, Telegram, Matrix, or other platforms.
|
|
||||||
|
|||||||
629
SLACK_SETUP.md
Normal file
629
SLACK_SETUP.md
Normal file
@@ -0,0 +1,629 @@
|
|||||||
|
# Slack Setup Guide
|
||||||
|
|
||||||
|
This guide walks through setting up Govbot for a Slack channel with channel-scoped governance.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Slack adapter enables **channel-scoped governance** where the bot manages a single channel without requiring workspace admin permissions. This makes it ideal for:
|
||||||
|
|
||||||
|
- Department or team governance within larger organizations
|
||||||
|
- Project-specific decision-making channels
|
||||||
|
- Community spaces within company Slack workspaces
|
||||||
|
- Testing governance patterns before workspace-wide deployment
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Slack workspace (free or paid tier)
|
||||||
|
- Permission to create Slack apps in the workspace
|
||||||
|
- Python 3.11+ installed
|
||||||
|
- Ollama (for local models) or API keys for cloud models
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
The Slack adapter provides **16 governance skills** across 5 categories:
|
||||||
|
|
||||||
|
### Channel Access Control
|
||||||
|
- Invite/remove users from the governance channel
|
||||||
|
- Control who participates in governance
|
||||||
|
|
||||||
|
### Channel Management
|
||||||
|
- Create new channels for working groups
|
||||||
|
- Archive/unarchive channels
|
||||||
|
- Set channel topic and purpose
|
||||||
|
- Rename channels
|
||||||
|
|
||||||
|
### User Group Management (Channel "Roles")
|
||||||
|
- Create user groups as governance roles
|
||||||
|
- Add/remove users from groups
|
||||||
|
- Enable/disable groups
|
||||||
|
- Use groups to tag teams (e.g., @moderators, @council)
|
||||||
|
|
||||||
|
### Message Management
|
||||||
|
- Pin important governance decisions
|
||||||
|
- Unpin outdated information
|
||||||
|
|
||||||
|
### Content Moderation
|
||||||
|
- Delete messages (requires user:write scope)
|
||||||
|
|
||||||
|
## Platform Limitations
|
||||||
|
|
||||||
|
**Workspace Admin Only** (not available via channel bot):
|
||||||
|
- User account management (deactivate, invite new users)
|
||||||
|
- Workspace settings
|
||||||
|
- Emoji management
|
||||||
|
- Billing and plans
|
||||||
|
|
||||||
|
**Not Possible via API**:
|
||||||
|
- Role/permission management (owners, admins)
|
||||||
|
- Slack Connect (external organizations)
|
||||||
|
- Enterprise Grid features
|
||||||
|
- Workflow management
|
||||||
|
|
||||||
|
The bot is aware of these limitations and will explain them to users when requested.
|
||||||
|
|
||||||
|
## Step 1: Create Slack App
|
||||||
|
|
||||||
|
### 1.1 Create App
|
||||||
|
|
||||||
|
1. Go to [Slack API Dashboard](https://api.slack.com/apps)
|
||||||
|
2. Click **"Create New App"**
|
||||||
|
3. Choose **"From scratch"**
|
||||||
|
4. Enter:
|
||||||
|
- **App Name**: Govbot
|
||||||
|
- **Workspace**: Select your workspace
|
||||||
|
5. Click **"Create App"**
|
||||||
|
|
||||||
|
### 1.2 Configure Bot Token Scopes
|
||||||
|
|
||||||
|
Navigate to **OAuth & Permissions** → **Scopes** → **Bot Token Scopes**
|
||||||
|
|
||||||
|
Add these scopes:
|
||||||
|
|
||||||
|
**Essential (Required)**:
|
||||||
|
- `app_mentions:read` - Receive mentions of the bot
|
||||||
|
- `chat:write` - Post messages
|
||||||
|
- `channels:read` - View basic channel info
|
||||||
|
- `channels:manage` - Manage channels (rename, archive, etc.)
|
||||||
|
- `channels:history` - Read message history
|
||||||
|
- `users:read` - Get user information
|
||||||
|
- `usergroups:read` - View user groups
|
||||||
|
- `usergroups:write` - Create and manage user groups
|
||||||
|
- `pins:write` - Pin/unpin messages
|
||||||
|
|
||||||
|
**Optional (Enhanced Features)**:
|
||||||
|
- `channels:write` - Create new channels
|
||||||
|
- `chat:write.public` - Post to channels bot isn't in
|
||||||
|
- `im:read` - Read direct messages
|
||||||
|
- `im:write` - Send direct messages
|
||||||
|
- `im:history` - Read DM history
|
||||||
|
|
||||||
|
**Moderation (If Needed)**:
|
||||||
|
- `chat:write.customize` - Post as other users (for announcements)
|
||||||
|
- `users:write` - Modify user accounts (requires admin)
|
||||||
|
|
||||||
|
### 1.3 Enable Socket Mode
|
||||||
|
|
||||||
|
1. Navigate to **Socket Mode** in the sidebar
|
||||||
|
2. Click **"Enable Socket Mode"**
|
||||||
|
3. When prompted, create an app-level token:
|
||||||
|
- **Token Name**: govbot-socket
|
||||||
|
- **Scopes**: `connections:write`
|
||||||
|
4. **Save the app token** (starts with `xapp-`) - you'll need this
|
||||||
|
|
||||||
|
### 1.4 Enable Events
|
||||||
|
|
||||||
|
1. Navigate to **Event Subscriptions**
|
||||||
|
2. Toggle **"Enable Events"** to On
|
||||||
|
3. Under **Subscribe to bot events**, add:
|
||||||
|
- `app_mention` - When someone mentions the bot
|
||||||
|
- `message.im` - Direct messages to the bot
|
||||||
|
4. Click **"Save Changes"**
|
||||||
|
|
||||||
|
### 1.5 Install App to Workspace
|
||||||
|
|
||||||
|
1. Navigate to **Install App**
|
||||||
|
2. Click **"Install to Workspace"**
|
||||||
|
3. Review permissions and click **"Allow"**
|
||||||
|
4. **Save the Bot User OAuth Token** (starts with `xoxb-`) - you'll need this
|
||||||
|
|
||||||
|
## Step 2: Add Bot to Channel
|
||||||
|
|
||||||
|
### 2.1 Create or Select Governance Channel
|
||||||
|
|
||||||
|
1. Create a new channel (e.g., `#governance`) or select existing channel
|
||||||
|
2. Invite the bot to the channel:
|
||||||
|
```
|
||||||
|
/invite @Govbot
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Get Channel ID
|
||||||
|
|
||||||
|
**Method 1: Via Channel Details**
|
||||||
|
1. Right-click the channel name
|
||||||
|
2. Click "View channel details"
|
||||||
|
3. Scroll to bottom - Channel ID is shown
|
||||||
|
4. Copy the ID (format: `C0123456789`)
|
||||||
|
|
||||||
|
**Method 2: Via URL**
|
||||||
|
- When viewing the channel, the URL contains the ID:
|
||||||
|
`https://app.slack.com/client/T.../C0123456789`
|
||||||
|
(The part after the last `/` starting with `C`)
|
||||||
|
|
||||||
|
## Step 3: Configure Govbot
|
||||||
|
|
||||||
|
### 3.1 Update Configuration File
|
||||||
|
|
||||||
|
1. **Copy the example config**:
|
||||||
|
```bash
|
||||||
|
cp config/config.example.yaml config/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Edit `config/config.yaml`**:
|
||||||
|
```yaml
|
||||||
|
platform:
|
||||||
|
type: slack
|
||||||
|
|
||||||
|
slack:
|
||||||
|
bot_token: xoxb-your-bot-oauth-token-here
|
||||||
|
app_token: xapp-your-app-level-token-here
|
||||||
|
channel_id: C0123456789 # Your governance channel ID
|
||||||
|
|
||||||
|
ai:
|
||||||
|
# For local models:
|
||||||
|
default_model: llama3.2
|
||||||
|
|
||||||
|
# For cloud models:
|
||||||
|
# default_model: gpt-4o-mini
|
||||||
|
# openai_api_key: your-key-here
|
||||||
|
# (or configure with: llm keys set openai)
|
||||||
|
|
||||||
|
governance:
|
||||||
|
constitution_path: constitution.md
|
||||||
|
db_path: govbot.db
|
||||||
|
default_veto_threshold: 0.67
|
||||||
|
enable_auto_execution: true
|
||||||
|
require_confirmation_for:
|
||||||
|
- admin_action
|
||||||
|
- moderation
|
||||||
|
|
||||||
|
debug: false
|
||||||
|
log_level: INFO
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Test LLM configuration**:
|
||||||
|
```bash
|
||||||
|
# Activate virtual environment
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Test that llm works
|
||||||
|
llm "Hello, test message"
|
||||||
|
|
||||||
|
# If using local models, verify Ollama is running
|
||||||
|
ollama list
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Initialize Database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Activate virtual environment
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Create the database
|
||||||
|
python -c "from src.govbot.db.models import init_db; init_db('govbot.db'); print('Database initialized!')"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Test with CLI
|
||||||
|
|
||||||
|
Before connecting to Slack, test the bot logic:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the CLI
|
||||||
|
python -m src.govbot
|
||||||
|
|
||||||
|
# Try these commands:
|
||||||
|
query What are the rules for proposals?
|
||||||
|
propose We should have weekly team meetings
|
||||||
|
processes
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 6: Install Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Activate virtual environment
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Install/update dependencies (includes slack-sdk)
|
||||||
|
uv pip install -e .
|
||||||
|
# Or: pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 7: Run the Bot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Activate virtual environment
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Start the bot
|
||||||
|
python -m src.govbot.bot
|
||||||
|
|
||||||
|
# You should see:
|
||||||
|
# INFO - Creating slack platform adapter...
|
||||||
|
# INFO - Connected to platform successfully
|
||||||
|
# INFO - Started listening for messages
|
||||||
|
# INFO - Bot is running. Press Ctrl+C to stop.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 8: Test on Slack
|
||||||
|
|
||||||
|
### Basic Test
|
||||||
|
|
||||||
|
In your governance channel, type:
|
||||||
|
```
|
||||||
|
@Govbot help
|
||||||
|
```
|
||||||
|
|
||||||
|
The bot should respond with information about governance commands.
|
||||||
|
|
||||||
|
### Create a Proposal
|
||||||
|
|
||||||
|
```
|
||||||
|
@Govbot I propose we add a weekly team retrospective on Fridays
|
||||||
|
```
|
||||||
|
|
||||||
|
The bot should:
|
||||||
|
- Acknowledge the proposal
|
||||||
|
- Cite constitutional authority
|
||||||
|
- Explain voting period and threshold
|
||||||
|
- Provide voting instructions
|
||||||
|
|
||||||
|
### Vote on Proposal
|
||||||
|
|
||||||
|
In a thread reply or new message:
|
||||||
|
```
|
||||||
|
@Govbot agree
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Status
|
||||||
|
|
||||||
|
```
|
||||||
|
@Govbot status 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Platform Skills
|
||||||
|
|
||||||
|
```
|
||||||
|
@Govbot what actions can you perform in this channel?
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a User Group (Role)
|
||||||
|
|
||||||
|
```
|
||||||
|
@Govbot create a user group called "council" for governance leadership
|
||||||
|
```
|
||||||
|
|
||||||
|
## Platform Skills Reference
|
||||||
|
|
||||||
|
### Channel Access
|
||||||
|
- **invite_to_channel**: Add user to governance channel
|
||||||
|
- **remove_from_channel**: Remove user from channel
|
||||||
|
|
||||||
|
### Channel Management
|
||||||
|
- **create_channel**: Create new channel for working groups
|
||||||
|
- **archive_channel**: Archive a channel
|
||||||
|
- **unarchive_channel**: Restore archived channel
|
||||||
|
- **set_channel_topic**: Update channel topic
|
||||||
|
- **set_channel_purpose**: Update channel description
|
||||||
|
- **rename_channel**: Change channel name
|
||||||
|
|
||||||
|
### User Groups (Roles)
|
||||||
|
- **create_user_group**: Create governance role (e.g., @moderators)
|
||||||
|
- **add_to_user_group**: Add user to role
|
||||||
|
- **remove_from_user_group**: Remove user from role
|
||||||
|
- **disable_user_group**: Disable a role
|
||||||
|
|
||||||
|
### Message Management
|
||||||
|
- **pin_message**: Pin important decisions
|
||||||
|
- **unpin_message**: Unpin messages
|
||||||
|
- **delete_message**: Remove inappropriate content
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Bot Not Responding
|
||||||
|
|
||||||
|
**Check**:
|
||||||
|
- Bot is invited to the channel (`/invite @Govbot`)
|
||||||
|
- Bot token (xoxb-...) is correct
|
||||||
|
- App token (xapp-...) is correct
|
||||||
|
- Socket Mode is enabled
|
||||||
|
- Events are subscribed (`app_mention`, `message.im`)
|
||||||
|
- Bot process is running (check logs)
|
||||||
|
|
||||||
|
**Test manually**:
|
||||||
|
```python
|
||||||
|
from slack_sdk import WebClient
|
||||||
|
|
||||||
|
client = WebClient(token="xoxb-your-token")
|
||||||
|
response = client.auth_test()
|
||||||
|
print(f"Bot ID: {response['user_id']}")
|
||||||
|
print(f"Bot Name: {response['user']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Socket Mode Connection Errors
|
||||||
|
|
||||||
|
**Check**:
|
||||||
|
- App token has `connections:write` scope
|
||||||
|
- No firewall blocking WebSocket connections
|
||||||
|
- Correct app token (not bot token)
|
||||||
|
|
||||||
|
**Enable debug logging**:
|
||||||
|
```yaml
|
||||||
|
debug: true
|
||||||
|
log_level: DEBUG
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission Errors
|
||||||
|
|
||||||
|
**Common issues**:
|
||||||
|
- Missing bot token scopes (check OAuth & Permissions)
|
||||||
|
- Bot not invited to channel
|
||||||
|
- User attempting action they don't have permission for
|
||||||
|
|
||||||
|
**Verify scopes**:
|
||||||
|
```python
|
||||||
|
from slack_sdk import WebClient
|
||||||
|
|
||||||
|
client = WebClient(token="xoxb-your-token")
|
||||||
|
response = client.auth_test()
|
||||||
|
print(response) # Shows which scopes are active
|
||||||
|
```
|
||||||
|
|
||||||
|
### Channel ID Issues
|
||||||
|
|
||||||
|
**Symptoms**: Bot responds but actions fail
|
||||||
|
|
||||||
|
**Fix**: Verify channel ID
|
||||||
|
```python
|
||||||
|
from slack_sdk import WebClient
|
||||||
|
|
||||||
|
client = WebClient(token="xoxb-your-token")
|
||||||
|
response = client.conversations_list()
|
||||||
|
for channel in response['channels']:
|
||||||
|
print(f"{channel['name']}: {channel['id']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### LLM Errors
|
||||||
|
|
||||||
|
**For local models**:
|
||||||
|
```bash
|
||||||
|
# Check Ollama is running
|
||||||
|
ollama list
|
||||||
|
ollama ps
|
||||||
|
|
||||||
|
# Test the model
|
||||||
|
llm -m llama3.2 "test"
|
||||||
|
```
|
||||||
|
|
||||||
|
**For cloud models**:
|
||||||
|
```bash
|
||||||
|
# Check API keys
|
||||||
|
llm keys list
|
||||||
|
|
||||||
|
# Test the model
|
||||||
|
llm -m gpt-4o-mini "test"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Errors
|
||||||
|
|
||||||
|
**Reset database**:
|
||||||
|
```bash
|
||||||
|
# Backup first!
|
||||||
|
cp govbot.db govbot.db.backup
|
||||||
|
|
||||||
|
# Delete and reinitialize
|
||||||
|
rm govbot.db
|
||||||
|
python -c "from src.govbot.db.models import init_db; init_db('govbot.db')"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
**📖 See [SECURITY.md](SECURITY.md) for the complete security guide.**
|
||||||
|
|
||||||
|
### Credentials
|
||||||
|
|
||||||
|
- **Never commit** `config/config.yaml` to version control (it's in `.gitignore`)
|
||||||
|
- Store tokens securely
|
||||||
|
- Use environment variables for production:
|
||||||
|
```bash
|
||||||
|
export GOVBOT_PLATFORM__SLACK__BOT_TOKEN="xoxb-..."
|
||||||
|
export GOVBOT_PLATFORM__SLACK__APP_TOKEN="xapp-..."
|
||||||
|
export GOVBOT_PLATFORM__SLACK__CHANNEL_ID="C0123456789"
|
||||||
|
```
|
||||||
|
- Rotate tokens periodically via Slack App dashboard
|
||||||
|
- Use separate apps for development and production
|
||||||
|
|
||||||
|
### Access Control
|
||||||
|
|
||||||
|
- Limit bot scopes to minimum required
|
||||||
|
- Don't grant unnecessary permissions
|
||||||
|
- Monitor bot actions through Slack audit logs
|
||||||
|
- Review user group membership regularly
|
||||||
|
- Use channel-specific governance (not workspace-wide)
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
|
||||||
|
Slack has rate limits for API calls:
|
||||||
|
- **Tier 1**: 1 request per second (most methods)
|
||||||
|
- **Tier 2**: 20 requests per minute (sending messages)
|
||||||
|
- **Tier 3**: 50 requests per minute (most read operations)
|
||||||
|
|
||||||
|
The bot handles rate limits automatically with retries, but be aware for high-volume governance.
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
### Systemd Service
|
||||||
|
|
||||||
|
Create `/etc/systemd/system/govbot.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Govbot Governance Bot (Slack)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=govbot
|
||||||
|
WorkingDirectory=/home/govbot/agentic-govbot
|
||||||
|
ExecStart=/usr/bin/python3 -m src.govbot.bot
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
Environment="GOVBOT_PLATFORM__SLACK__BOT_TOKEN=xoxb-..."
|
||||||
|
Environment="GOVBOT_PLATFORM__SLACK__APP_TOKEN=xapp-..."
|
||||||
|
Environment="GOVBOT_PLATFORM__SLACK__CHANNEL_ID=C0123456789"
|
||||||
|
Environment="GOVBOT_LOG_LEVEL=INFO"
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start:
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable govbot
|
||||||
|
sudo systemctl start govbot
|
||||||
|
sudo systemctl status govbot
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
sudo journalctl -u govbot -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Deployment
|
||||||
|
|
||||||
|
Create `Dockerfile`:
|
||||||
|
```dockerfile
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN pip install -e .
|
||||||
|
|
||||||
|
CMD ["python", "-m", "src.govbot.bot"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Build and run:
|
||||||
|
```bash
|
||||||
|
docker build -t govbot .
|
||||||
|
docker run \
|
||||||
|
-e GOVBOT_PLATFORM__SLACK__BOT_TOKEN=xoxb-... \
|
||||||
|
-e GOVBOT_PLATFORM__SLACK__APP_TOKEN=xapp-... \
|
||||||
|
-e GOVBOT_PLATFORM__SLACK__CHANNEL_ID=C0123456789 \
|
||||||
|
-v $(pwd)/config:/app/config \
|
||||||
|
-v $(pwd)/govbot.db:/app/govbot.db \
|
||||||
|
govbot
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
|
||||||
|
- Monitor Socket Mode connection health
|
||||||
|
- Track API rate limit usage
|
||||||
|
- Log all governance actions
|
||||||
|
- Alert on connection failures
|
||||||
|
- Monitor database growth
|
||||||
|
- Track user group membership changes
|
||||||
|
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
### Multiple Channels
|
||||||
|
|
||||||
|
To manage multiple governance channels, run multiple bot instances:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# config/governance-channel-1.yaml
|
||||||
|
platform:
|
||||||
|
type: slack
|
||||||
|
slack:
|
||||||
|
bot_token: xoxb-same-token
|
||||||
|
app_token: xapp-same-token
|
||||||
|
channel_id: C0111111111 # Different channel
|
||||||
|
|
||||||
|
governance:
|
||||||
|
db_path: govbot-channel1.db # Separate database
|
||||||
|
```
|
||||||
|
|
||||||
|
### User Groups as Roles
|
||||||
|
|
||||||
|
User groups act as channel "roles":
|
||||||
|
|
||||||
|
```
|
||||||
|
# Create governance roles
|
||||||
|
@Govbot create user group "moderators"
|
||||||
|
@Govbot create user group "council"
|
||||||
|
@Govbot create user group "working-group-a"
|
||||||
|
|
||||||
|
# Assign members
|
||||||
|
@Govbot add @alice to moderators
|
||||||
|
@Govbot add @bob to council
|
||||||
|
|
||||||
|
# Use in governance
|
||||||
|
"Only @moderators can approve reports"
|
||||||
|
"@council members have veto power"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Constitution
|
||||||
|
|
||||||
|
Edit `constitution.md` to define Slack-specific governance:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Slack Governance
|
||||||
|
|
||||||
|
### Channel Roles
|
||||||
|
- **@moderators**: Handle reports and moderation
|
||||||
|
- **@council**: Make policy decisions
|
||||||
|
- **Members**: All channel participants
|
||||||
|
|
||||||
|
### Channel Management
|
||||||
|
New channels require:
|
||||||
|
1. Proposal with clear purpose
|
||||||
|
2. 2/3 approval from @council
|
||||||
|
3. Assignment of channel owner
|
||||||
|
|
||||||
|
### User Groups
|
||||||
|
- Creation requires council approval
|
||||||
|
- Membership changes logged publicly
|
||||||
|
- Groups reviewed quarterly
|
||||||
|
```
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Customize the constitution for your team/department
|
||||||
|
- Test governance workflows with your team
|
||||||
|
- Create initial user groups (roles)
|
||||||
|
- Set up announcement patterns
|
||||||
|
- Document your Slack governance process
|
||||||
|
- Gather feedback and iterate
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- Check [README.md](README.md) for general architecture
|
||||||
|
- Review [PLATFORMS.md](PLATFORMS.md) for platform details
|
||||||
|
- See [MASTODON_SETUP.md](MASTODON_SETUP.md) for comparison
|
||||||
|
- Review [Slack API Documentation](https://api.slack.com/docs)
|
||||||
|
- Open an issue on GitHub for bugs or questions
|
||||||
|
|
||||||
|
## Comparison: Slack vs Mastodon
|
||||||
|
|
||||||
|
| Feature | Slack | Mastodon |
|
||||||
|
|---------|-------|----------|
|
||||||
|
| **Scope** | Channel-scoped | Instance-wide |
|
||||||
|
| **Admin Powers** | Limited to channel | Full instance admin |
|
||||||
|
| **Setup** | OAuth app + tokens | OAuth + instance admin |
|
||||||
|
| **User Management** | User groups only | Full account management |
|
||||||
|
| **Moderation** | Message deletion | Suspend, silence, ban |
|
||||||
|
| **Federation** | No | Yes (block domains) |
|
||||||
|
| **Rate Limits** | Tiered, generous | Per instance config |
|
||||||
|
| **Best For** | Team/dept governance | Community instance governance |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Important**: This is governance infrastructure for your team. Test thoroughly with a small group before deploying to larger channels!
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
platform:
|
platform:
|
||||||
# Platform type: mastodon, discord, telegram, mock
|
# Platform type: mastodon, slack, discord, telegram, mock
|
||||||
type: mastodon
|
type: mastodon
|
||||||
|
|
||||||
# Mastodon configuration (if using Mastodon)
|
# Mastodon configuration (if using Mastodon)
|
||||||
@@ -10,6 +10,12 @@ platform:
|
|||||||
access_token: your_access_token_here
|
access_token: your_access_token_here
|
||||||
bot_username: govbot
|
bot_username: govbot
|
||||||
|
|
||||||
|
# Slack configuration (if using Slack)
|
||||||
|
# slack:
|
||||||
|
# bot_token: xoxb-your-bot-oauth-token-here
|
||||||
|
# app_token: xapp-your-app-level-token-here
|
||||||
|
# channel_id: C0123456789
|
||||||
|
|
||||||
# Discord configuration (for future use)
|
# Discord configuration (for future use)
|
||||||
# discord:
|
# discord:
|
||||||
# token: your_discord_bot_token
|
# token: your_discord_bot_token
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ readme = "README.md"
|
|||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mastodon.py>=1.8.0",
|
"mastodon.py>=1.8.0",
|
||||||
|
"slack-sdk>=3.33.0",
|
||||||
"sqlalchemy>=2.0.0",
|
"sqlalchemy>=2.0.0",
|
||||||
"pydantic>=2.0.0",
|
"pydantic>=2.0.0",
|
||||||
"pydantic-settings>=2.0.0",
|
"pydantic-settings>=2.0.0",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Core dependencies
|
# Core dependencies
|
||||||
Mastodon.py>=1.8.0
|
Mastodon.py>=1.8.0
|
||||||
|
slack-sdk>=3.33.0
|
||||||
SQLAlchemy>=2.0.0
|
SQLAlchemy>=2.0.0
|
||||||
llm>=0.13.0
|
llm>=0.13.0
|
||||||
llm-anthropic>=0.23
|
llm-anthropic>=0.23
|
||||||
|
|||||||
104
scripts/publish_constitution.py
Executable file
104
scripts/publish_constitution.py
Executable file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/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()
|
||||||
@@ -61,7 +61,8 @@ class GovernanceAgent:
|
|||||||
request: str,
|
request: str,
|
||||||
actor: str,
|
actor: str,
|
||||||
context: Optional[Dict[str, Any]] = None,
|
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]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Process a governance request using agentic interpretation.
|
Process a governance request using agentic interpretation.
|
||||||
@@ -80,6 +81,7 @@ class GovernanceAgent:
|
|||||||
actor: Who made the request
|
actor: Who made the request
|
||||||
context: Optional context (thread ID, etc.)
|
context: Optional context (thread ID, etc.)
|
||||||
platform_skills: List of available platform-specific skills
|
platform_skills: List of available platform-specific skills
|
||||||
|
platform_limitations: Information about what's not possible via API
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response dictionary with action taken and audit trail
|
Response dictionary with action taken and audit trail
|
||||||
@@ -106,7 +108,8 @@ class GovernanceAgent:
|
|||||||
memory=memory_context,
|
memory=memory_context,
|
||||||
actor=actor,
|
actor=actor,
|
||||||
context=context,
|
context=context,
|
||||||
platform_skills=platform_skills
|
platform_skills=platform_skills,
|
||||||
|
platform_limitations=platform_limitations
|
||||||
)
|
)
|
||||||
|
|
||||||
# Step 5: Execute the decision
|
# Step 5: Execute the decision
|
||||||
@@ -225,7 +228,8 @@ Return your analysis as JSON:
|
|||||||
memory: Dict[str, Any],
|
memory: Dict[str, Any],
|
||||||
actor: str,
|
actor: str,
|
||||||
context: Optional[Dict[str, Any]],
|
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]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Use LLM to decide what action to take.
|
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"
|
platform_skills_info = "\n\nAVAILABLE PLATFORM SKILLS:\n"
|
||||||
for skill in platform_skills:
|
for skill in platform_skills:
|
||||||
params_str = ", ".join([f"{p['name']}: {p['type']}" for p in skill.get('parameters', [])])
|
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"- {skill['name']}({params_str}): {skill['description']}\n"
|
||||||
|
platform_skills_info += f" Category: {category}\n"
|
||||||
if skill.get('constitutional_authorization'):
|
if skill.get('constitutional_authorization'):
|
||||||
platform_skills_info += f" Authorization: {skill['constitutional_authorization']}\n"
|
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.
|
prompt = f"""You are a governance bot interpreting a community constitution.
|
||||||
|
|
||||||
IMPORTANT: When generating the "response" field, use newline characters (\\n) for line breaks:
|
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)}
|
{json.dumps(memory, indent=2)}
|
||||||
|
|
||||||
ACTOR: {actor}
|
ACTOR: {actor}
|
||||||
{platform_skills_info}
|
{platform_skills_info}{platform_limitations_info}
|
||||||
Based on the constitution and current state, decide what action to take.
|
Based on the constitution and current state, decide what action to take.
|
||||||
|
|
||||||
IMPORTANT AUTHORITY CHECK:
|
IMPORTANT AUTHORITY CHECK:
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from .agent import GovernanceAgent
|
|||||||
from .scheduler import GovernanceScheduler
|
from .scheduler import GovernanceScheduler
|
||||||
from .platforms.base import PlatformAdapter, PlatformMessage, MockPlatformAdapter
|
from .platforms.base import PlatformAdapter, PlatformMessage, MockPlatformAdapter
|
||||||
from .platforms.mastodon import MastodonAdapter
|
from .platforms.mastodon import MastodonAdapter
|
||||||
|
from .platforms.slack import SlackAdapter
|
||||||
|
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
@@ -125,12 +126,14 @@ class Govbot:
|
|||||||
|
|
||||||
if platform_type == "mastodon":
|
if platform_type == "mastodon":
|
||||||
return MastodonAdapter(self.config.platform.mastodon.model_dump())
|
return MastodonAdapter(self.config.platform.mastodon.model_dump())
|
||||||
|
elif platform_type == "slack":
|
||||||
|
return SlackAdapter(self.config.platform.slack.model_dump())
|
||||||
elif platform_type == "mock":
|
elif platform_type == "mock":
|
||||||
return MockPlatformAdapter({})
|
return MockPlatformAdapter({})
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unknown platform type: {platform_type}. "
|
f"Unknown platform type: {platform_type}. "
|
||||||
f"Supported: mastodon, mock"
|
f"Supported: mastodon, slack, mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -199,6 +202,7 @@ class Govbot:
|
|||||||
|
|
||||||
# Get available platform skills
|
# Get available platform skills
|
||||||
platform_skills_list = []
|
platform_skills_list = []
|
||||||
|
platform_limitations = None
|
||||||
try:
|
try:
|
||||||
platform_skills = self.platform.get_skills()
|
platform_skills = self.platform.get_skills()
|
||||||
# Convert PlatformSkill objects to dicts for the agent
|
# Convert PlatformSkill objects to dicts for the agent
|
||||||
@@ -214,6 +218,10 @@ class Govbot:
|
|||||||
"constitutional_authorization": skill.constitutional_authorization,
|
"constitutional_authorization": skill.constitutional_authorization,
|
||||||
}
|
}
|
||||||
platform_skills_list.append(skill_dict)
|
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:
|
except Exception as e:
|
||||||
logger.warning(f"Could not get platform skills: {e}")
|
logger.warning(f"Could not get platform skills: {e}")
|
||||||
|
|
||||||
@@ -222,6 +230,7 @@ class Govbot:
|
|||||||
actor=f"@{message.author_handle}",
|
actor=f"@{message.author_handle}",
|
||||||
context=context,
|
context=context,
|
||||||
platform_skills=platform_skills_list if platform_skills_list else None,
|
platform_skills=platform_skills_list if platform_skills_list else None,
|
||||||
|
platform_limitations=platform_limitations,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if we need to execute a platform skill
|
# 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")
|
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):
|
class AIConfig(BaseModel):
|
||||||
"""AI model configuration"""
|
"""AI model configuration"""
|
||||||
|
|
||||||
@@ -58,8 +67,9 @@ class GovernanceConfig(BaseModel):
|
|||||||
class PlatformConfig(BaseModel):
|
class PlatformConfig(BaseModel):
|
||||||
"""Platform selection and configuration"""
|
"""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")
|
mastodon: Optional[MastodonConfig] = Field(None, description="Mastodon configuration")
|
||||||
|
slack: Optional[SlackConfig] = Field(None, description="Slack configuration")
|
||||||
# Future platforms:
|
# Future platforms:
|
||||||
# discord: Optional[DiscordConfig] = None
|
# discord: Optional[DiscordConfig] = None
|
||||||
# telegram: Optional[TelegramConfig] = None
|
# telegram: Optional[TelegramConfig] = None
|
||||||
@@ -117,7 +127,7 @@ def create_example_config(output_path: str = "config/config.example.yaml"):
|
|||||||
"""
|
"""
|
||||||
example_config = {
|
example_config = {
|
||||||
"platform": {
|
"platform": {
|
||||||
"type": "mastodon", # or "discord", "telegram", "mock"
|
"type": "mastodon", # or "slack", "discord", "telegram", "mock"
|
||||||
"mastodon": {
|
"mastodon": {
|
||||||
"instance_url": "https://your-mastodon-instance.social",
|
"instance_url": "https://your-mastodon-instance.social",
|
||||||
"client_id": "your_client_id_here",
|
"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",
|
"access_token": "your_access_token_here",
|
||||||
"bot_username": "govbot",
|
"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 example (for future use):
|
||||||
# "discord": {
|
# "discord": {
|
||||||
# "token": "your_discord_bot_token",
|
# "token": "your_discord_bot_token",
|
||||||
|
|||||||
Reference in New Issue
Block a user