```{toctree}
---
maxdepth: 2
hidden: true
---
docs/src/installation.md
docs/src/quickstart.md
docs/src/base-models.md
docs/src/backends.md
docs/src/backend-config.md
docs/src/messaging.md
docs/src/mentions.md
docs/src/reactions-threads.md
docs/src/format-system.md
docs/src/advanced-features.md
docs/src/conversion.md
docs/src/csp-integration.md
docs/src/examples.md
docs/src/integration-testing.md
docs/src/api.md
```
chatom
Framework-agnostic chat application models and utilities
[](https://github.com/1kbgz/chatom/actions/workflows/build.yaml)
[](https://codecov.io/gh/1kbgz/chatom)
[](https://github.com/1kbgz/chatom)
[](https://pypi.python.org/pypi/chatom)
## Overview
`chatom` provides a unified, framework-agnostic representation of chat applications. It offers:
- **Base models** for users, channels, messages, attachments, embeds, reactions, and more
- **Backend-specific implementations** for Discord, Slack, Symphony, and Telegram
- **Rich text formatting** with nodes for bold, italic, code, tables, lists, and more
- **Format converters** to render messages as plaintext, markdown, HTML, Slack mrkdwn, Discord markdown, or Symphony MessageML
## Installation
```bash
pip install chatom
```
## Quick Start
### Basic Models
```python
from chatom import User, Channel, Message, Emoji, Reaction
# Create a user
user = User(id="u123", name="Alice", email="alice@example.com")
# Create a channel
channel = Channel(id="c456", name="general", topic="General discussion")
# Create a message with reactions
emoji = Emoji(name="thumbsup", unicode="👍")
reaction = Reaction(emoji=emoji, count=5)
message = Message(
id="m789",
content="Hello, world!",
author=user,
channel=channel,
reactions=[reaction],
)
```
### Rich Text Formatting
```python
from chatom import (
Format,
Text,
Bold,
Italic,
Paragraph,
Span,
Table,
FormattedMessage,
MessageBuilder,
)
# Build a formatted message using nodes
msg = FormattedMessage(
content=[
Paragraph(children=[
Text(content="Welcome to "),
Bold(child=Text(content="chatom")),
Text(content="!"),
]),
]
)
# Render to different formats
print(msg.render(Format.MARKDOWN)) # "Welcome to **chatom**!\n"
print(msg.render(Format.HTML)) # "Welcome to chatom!
"
print(msg.render(Format.SLACK_MARKDOWN)) # "Welcome to *chatom*!\n"
```
### Tables
```python
from chatom import Table, Format
# Create a table from data
data = [
["Alice", "100", "Gold"],
["Bob", "85", "Silver"],
["Carol", "70", "Bronze"],
]
table = Table.from_data(data, headers=["Name", "Score", "Rank"])
# Render as markdown
print(table.render(Format.MARKDOWN))
# | Name | Score | Rank |
# |---|---|---|
# | Alice | 100 | Gold |
# | Bob | 85 | Silver |
# | Carol | 70 | Bronze |
# Render as HTML
print(table.render(Format.HTML))
#
```
### Backend-Specific Models
Each backend provides specialized models with platform-specific fields:
```python
# Discord
from chatom.discord import (
DiscordUser,
DiscordChannel,
DiscordChannelType,
DiscordPresence,
mention_user,
mention_channel,
mention_role,
mention_everyone,
mention_here,
)
user = DiscordUser(
id="123456789",
name="Alice",
discriminator="1234",
is_bot=False,
)
print(mention_user(user)) # "<@123456789>"
channel = DiscordChannel(
id="987654321",
name="general",
channel_type=DiscordChannelType.GUILD_TEXT,
)
print(mention_channel(channel)) # "<#987654321>"
# Slack
from chatom.slack import (
SlackUser,
SlackChannel,
SlackPresence,
mention_user,
mention_channel,
mention_user_group,
mention_here,
mention_channel_all,
mention_everyone,
)
user = SlackUser(
id="U123456",
name="alice",
real_name="Alice Smith",
team_id="T123",
)
print(mention_user(user)) # "<@U123456>"
# Symphony
from chatom.symphony import (
SymphonyUser,
SymphonyChannel,
SymphonyStreamType,
mention_user,
format_hashtag,
format_cashtag,
)
user = SymphonyUser(id="123", name="alice", user_id=12345)
print(mention_user(user)) # ''
print(format_hashtag("python")) # ''
print(format_cashtag("AAPL")) # ''
# Telegram
from chatom.telegram import (
TelegramUser,
TelegramChannel,
TelegramChatType,
TelegramPresence,
mention_user,
mention_channel,
)
user = TelegramUser(id="123456", name="Alice", username="alice")
print(mention_user(user)) # "@alice"
channel = TelegramChannel(id="-1001234567890", name="General")
print(mention_channel(channel)) # "#General"
```
### Polymorphic Mentions
The `mention_user` and `mention_channel` functions use `singledispatch` to automatically route to the correct backend implementation:
```python
from chatom import mention_user
from chatom.discord import DiscordUser
from chatom.slack import SlackUser
from chatom.telegram import TelegramUser
discord_user = DiscordUser(id="123", name="alice")
slack_user = SlackUser(id="U123", name="alice")
telegram_user = TelegramUser(id="789", name="alice", username="alice")
print(mention_user(discord_user)) # "<@123>"
print(mention_user(slack_user)) # "<@U123>"
print(mention_user(telegram_user)) # "@alice"
```
### Backend-Agnostic Mentions
Use `mention_user_for_backend` and `mention_channel_for_backend` when you have a base User or Channel object and want to format it for a specific backend:
```python
from chatom import User, Channel, mention_user_for_backend, mention_channel_for_backend
user = User(id="123", name="Alice", email="alice@example.com")
channel = Channel(id="C456", name="general")
# Mention for different backends
print(mention_user_for_backend(user, "discord")) # "<@123>"
print(mention_user_for_backend(user, "slack")) # "<@123>"
print(mention_user_for_backend(user, "symphony")) # ''
print(mention_user_for_backend(user, "telegram")) # "@Alice"
print(mention_channel_for_backend(channel, "discord")) # "<#C456>"
print(mention_channel_for_backend(channel, "slack")) # "<#C456>"
```
### Rendering Messages for a Backend
Use `render_for` to render a formatted message using the appropriate format for a backend:
```python
from chatom import FormattedMessage, Paragraph, Text, Bold, get_format_for_backend
msg = FormattedMessage(
content=[
Paragraph(children=[
Text(content="Hello, "),
Bold(child=Text(content="world")),
Text(content="!"),
]),
]
)
# Render for different backends
print(msg.render_for("slack")) # "Hello, *world*!\n" (Slack mrkdwn)
print(msg.render_for("discord")) # "Hello, **world**!\n" (Discord markdown)
print(msg.render_for("symphony")) # "Hello, world!
" (MessageML)
print(msg.render_for("telegram")) # "Hello, world!" (HTML)
# Get the format for a backend
from chatom import BACKEND_FORMAT_MAP
print(BACKEND_FORMAT_MAP["slack"]) # Format.SLACK_MARKDOWN
```
### Type Conversion
Convert between base types and backend-specific types with validation:
```python
from chatom import (
User,
promote,
demote,
can_promote,
validate_for_backend,
DISCORD,
SLACK,
)
from chatom.discord import DiscordUser
# Create a base user
user = User(id="123", name="Alice", handle="alice")
# Check if it can be promoted
if can_promote(user, DISCORD):
# Promote to DiscordUser with extra fields
discord_user = promote(user, DISCORD, discriminator="1234")
print(discord_user.discriminator) # "1234"
print(type(discord_user)) #
# Demote back to base User
base_user = demote(discord_user)
print(type(base_user)) #
# Cross-backend conversion: Discord -> Slack
slack_user = promote(demote(discord_user), SLACK, team_id="T123")
print(slack_user.team_id) # "T123"
```
### Backend Capabilities
```python
from chatom import (
Capability,
DISCORD_CAPABILITIES,
SLACK_CAPABILITIES,
SYMPHONY_CAPABILITIES,
TELEGRAM_CAPABILITIES,
)
# Check what a backend supports
print(DISCORD_CAPABILITIES.supports(Capability.THREADS)) # True
print(DISCORD_CAPABILITIES.supports(Capability.VOICE_CHAT)) # True
print(DISCORD_CAPABILITIES.supports(Capability.EMOJI_REACTIONS)) # True
```
### Presence and Status
```python
from chatom import Presence, PresenceStatus
presence = Presence(
status=PresenceStatus.ONLINE,
status_text="Working on chatom",
activity="Coding",
)
print(presence.is_available) # True
print(presence.is_online) # True
```
### Connections and Registries
The `Connection` base class provides a foundation for backend connections, along with `UserRegistry` and `ChannelRegistry` for managing and looking up users and channels:
```python
from chatom import Connection, UserRegistry, ChannelRegistry, User, Channel, LookupError
# Create registries for managing users and channels
users = [
User(id="u1", name="Alice", email="alice@example.com", handle="alice"),
User(id="u2", name="Bob", email="bob@example.com", handle="bob"),
]
user_registry = UserRegistry(users=users)
# Look up users by different fields
print(user_registry.id_to_user("u1").name) # "Alice"
print(user_registry.email_to_user("bob@example.com").name) # "Bob"
print(user_registry.name_to_user("Alice").id) # "u1"
print(user_registry.handle_to_user("alice").email) # "alice@example.com"
# Reverse lookups
print(user_registry.user_to_id(users[0])) # "u1"
print(user_registry.user_to_email(users[0])) # "alice@example.com"
# Generic lookup - searches all fields
print(user_registry.lookup("alice").id) # "u1" (matches handle)
print(user_registry.lookup("bob@example.com").id) # "u2" (matches email)
# Check if a user exists
print(user_registry.get_user("u1") is not None) # True
print(user_registry.get_user("unknown")) # None (no exception)
# Same pattern for channels
channels = [
Channel(id="c1", name="general", topic="General discussion"),
Channel(id="c2", name="random"),
]
channel_registry = ChannelRegistry(channels=channels)
print(channel_registry.id_to_channel("c1").name) # "general"
print(channel_registry.name_to_channel("random").id) # "c2"
print(channel_registry.lookup("general").topic) # "General discussion"
```
Subclass `Connection` to implement backend-specific connection logic:
```python
from chatom import Connection, UserRegistry, ChannelRegistry
class MyBackendConnection(Connection):
"""Custom connection implementation."""
async def connect(self):
# Establish connection to your backend
pass
async def disconnect(self):
# Clean up connection
pass
async def fetch_users(self) -> UserRegistry:
# Fetch and return users from the backend
users = await self._fetch_users_from_api()
return UserRegistry(users=users)
async def fetch_channels(self) -> ChannelRegistry:
# Fetch and return channels from the backend
channels = await self._fetch_channels_from_api()
return ChannelRegistry(channels=channels)
```
## Supported Backends
| Backend | User Model | Channel Model | Mention Support | Presence |
| -------- | -------------- | ----------------- | --------------- | ------------------ |
| Discord | `DiscordUser` | `DiscordChannel` | ✅ | `DiscordPresence` |
| Slack | `SlackUser` | `SlackChannel` | ✅ | `SlackPresence` |
| Symphony | `SymphonyUser` | `SymphonyChannel` | ✅ | `SymphonyPresence` |
| Telegram | `TelegramUser` | `TelegramChannel` | ✅ | `TelegramPresence` |
## Output Formats
| Format | Enum Value | Description |
| ------------------ | --------------------------- | ----------------------------- |
| Plaintext | `Format.PLAINTEXT` | Plain text with no formatting |
| Markdown | `Format.MARKDOWN` | Standard Markdown |
| Slack mrkdwn | `Format.SLACK_MARKDOWN` | Slack's mrkdwn format |
| Discord Markdown | `Format.DISCORD_MARKDOWN` | Discord-flavored Markdown |
| HTML | `Format.HTML` | Standard HTML |
| Symphony MessageML | `Format.SYMPHONY_MESSAGEML` | Symphony's XML-based format |
## Text Node Types
| Node | Description | Example Output (Markdown) |
| ---------------- | --------------- | ------------------------------ |
| `Text` | Plain text | `Hello` |
| `Bold` | Bold text | `**Hello**` |
| `Italic` | Italic text | `*Hello*` |
| `Strikethrough` | Strikethrough | `~~Hello~~` |
| `Underline` | Underlined text | `Hello` (HTML only) |
| `Code` | Inline code | `` `code` `` |
| `CodeBlock` | Code block | ```` ```python\ncode\n``` ```` |
| `Link` | Hyperlink | `[text](url)` |
| `Quote` | Block quote | `> quoted text` |
| `Heading` | Heading (h1-h6) | `# Heading` |
| `Paragraph` | Paragraph | Content with newline |
| `UnorderedList` | Bullet list | `- item` |
| `OrderedList` | Numbered list | `1. item` |
| `Table` | Data table | Markdown/HTML table |
| `UserMention` | User mention | `@user` or `<@id>` |
| `ChannelMention` | Channel mention | `#channel` |
## Development
```bash
# Clone the repository
git clone https://github.com/1kbgz/chatom.git
cd chatom
# Install development dependencies
make develop
# Run tests
make test
# Run linting
make lint
```
## License
Apache License 2.0 - see [LICENSE](LICENSE) for details.