mattermost/examples/hello_python/README.md
Nick Misasi da0bda1d10 docs(hello_python): add critical venv bundling notes
- Use --copies flag when creating venv for distribution
- Symlinks point to build machine paths and won't work after extraction
- Avoid -e (editable) installs which reference local paths

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:03:59 -05:00

269 lines
7 KiB
Markdown

# Hello Python - Example Mattermost Plugin
A complete example demonstrating the Mattermost Python Plugin SDK.
## Overview
This plugin showcases the core features of the Python SDK:
- **Lifecycle Hooks**: `OnActivate` and `OnDeactivate` for plugin setup/teardown
- **Message Filtering**: `MessageWillBePosted` to inspect and modify messages
- **Slash Commands**: `ExecuteCommand` to handle custom `/hello` command
- **API Client**: Accessing Mattermost APIs via `self.api`
- **Logging**: Using `self.logger` for plugin logging
## Prerequisites
- Python 3.9 or higher
- Mattermost server with Python plugin support enabled
## Plugin Structure
```
hello_python/
plugin.json # Plugin manifest with Python runtime configuration
plugin.py # Main plugin implementation
requirements.txt # Python dependencies
README.md # This file
```
### plugin.json
The manifest declares this as a Python plugin:
```json
{
"server": {
"executable": "plugin.py",
"runtime": "python",
"python_version": ">=3.9"
}
}
```
Key fields:
- `server.executable`: The Python entry point script
- `server.runtime`: Set to `"python"` to indicate this is a Python plugin
- `server.python_version`: Minimum Python version required
### plugin.py
The main plugin implementation. Key patterns:
```python
from mattermost_plugin import Plugin, hook, HookName
class HelloPythonPlugin(Plugin):
@hook(HookName.OnActivate)
def on_activate(self) -> None:
self.logger.info("Plugin activated!")
version = self.api.get_server_version()
@hook(HookName.MessageWillBePosted)
def filter_message(self, context, post):
# Return (post, "") to allow, (None, "reason") to reject
return post, ""
@hook(HookName.ExecuteCommand)
def execute_command(self, context, args):
return {"response_type": "ephemeral", "text": "Hello!"}
if __name__ == "__main__":
from mattermost_plugin.server import run_plugin
run_plugin(HelloPythonPlugin)
```
## Installation (Development)
1. Create and activate a virtual environment:
```bash
cd examples/hello_python
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
2. Install the SDK in development mode:
```bash
pip install -e ../../python-sdk
```
3. Verify the plugin parses correctly:
```bash
python3 -c "import plugin; print('Plugin loaded successfully')"
```
## Installation (Production)
For production deployments, the SDK will be available via pip:
```bash
pip install mattermost-plugin-sdk
```
## Packaging
Python plugins are distributed as `.tar.gz` bundles. There are two packaging approaches:
### Option 1: System Python (smaller bundle, requires server-side setup)
Create a minimal bundle that relies on system Python:
```bash
cd examples/hello_python
tar -czvf hello-python-0.1.0.tar.gz \
plugin.json \
plugin.py \
requirements.txt
```
**Server requirement:** Install the SDK on the Mattermost server:
```bash
pip3 install mattermost-plugin-sdk
```
### Option 2: Bundled Virtual Environment (self-contained, larger bundle)
Create a fully self-contained bundle with all dependencies:
```bash
cd examples/hello_python
# Create virtual environment with --copies to include actual Python binaries
# (symlinks won't work when extracted on a different machine)
python3 -m venv --copies venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install mattermost-plugin-sdk # Or: pip install ../../python-sdk
pip install grpcio protobuf
deactivate
# Create the bundle
tar -czvf hello-python-0.1.0.tar.gz \
plugin.json \
plugin.py \
requirements.txt \
venv/
```
**Important notes:**
- Use `--copies` when creating the venv to include actual Python binaries (not symlinks)
- Do NOT use `-e` (editable install) for the SDK - it references local paths that won't exist on the server
- The bundled venv is Python version-specific. If your server has a different Python version than your build machine, use Option 1 or build the venv on a machine with matching Python version
## Deployment
### Via System Console (UI)
1. Log in as a System Admin
2. Go to **System Console → Plugins → Plugin Management**
3. Click **Upload Plugin**
4. Select `hello-python-0.1.0.tar.gz`
5. Click **Upload**
6. Find "Hello Python" in the plugin list and click **Enable**
### Via CLI (mmctl)
```bash
# Upload the plugin
mmctl plugin add hello-python-0.1.0.tar.gz
# Enable the plugin
mmctl plugin enable com.mattermost.hello-python
```
### Via API
```bash
# Upload
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "plugin=@hello-python-0.1.0.tar.gz" \
https://your-mattermost/api/v4/plugins
# Enable
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
https://your-mattermost/api/v4/plugins/com.mattermost.hello-python/enable
```
## Verifying Installation
After enabling the plugin, check the Mattermost server logs for:
```
Python plugin started with gRPC hooks
Hello Python plugin activated!
Connected to Mattermost server version: X.X.X
```
Test the plugin:
1. **Slash command:** Type `/hello` in any channel
2. **Message filter:** Post a message containing "badword" (should be blocked)
## Troubleshooting
| Issue | Solution |
|-------|----------|
| "Python interpreter not found" | Ensure `python3` is in PATH, or bundle a venv |
| "Module mattermost_plugin not found" | Install SDK on server or bundle venv |
| Plugin fails to start | Check server logs for gRPC handshake errors |
| Hooks not being called | Verify plugin implements `Implemented()` correctly |
| Bundled venv doesn't work | Python version mismatch; rebuild venv on server |
## Hooks Demonstrated
### OnActivate
Called when the plugin is enabled. Use for:
- Initializing plugin state
- Registering slash commands
- Connecting to external services
### OnDeactivate
Called when the plugin is disabled. Use for:
- Cleaning up resources
- Saving state
- Disconnecting from services
### MessageWillBePosted
Called before a message is posted. Return values:
- `(post, "")` - Allow the message (optionally modified)
- `(None, "reason")` - Reject the message with a reason
### ExecuteCommand
Called when a slash command is invoked. Return a response dict:
- `response_type`: `"ephemeral"` (private) or `"in_channel"` (public)
- `text`: The response message
## Running the Plugin
The plugin is designed to be run by the Mattermost server's Python plugin supervisor. The supervisor:
1. Starts the plugin process
2. Establishes gRPC communication
3. Invokes hooks as events occur
For standalone testing:
```bash
python plugin.py
```
Note: The plugin will output a go-plugin handshake line and wait for gRPC connections. In standalone mode, most functionality requires a connected Mattermost server.
## Next Steps
- Modify the word filter in `MessageWillBePosted`
- Add new slash commands in `ExecuteCommand`
- Implement additional hooks from `HookName` enum
- Use `self.api` to interact with Mattermost (users, channels, posts, etc.)
## Resources
- [Mattermost Plugin Documentation](https://developers.mattermost.com/extend/plugins/)
- [Python SDK API Reference](../../python-sdk/README.md)