Documentation Index
Fetch the complete documentation index at: https://mintlify.com/trustlessmatt/discord-exporter-bot/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Discord Exporter Bot generates digests in Obsidian-compatible markdown format with YAML frontmatter, bidirectional daily note links, and structured metadata. This allows seamless integration with Obsidian vaults for team knowledge management.
The format_obsidian_document function creates a fully-structured markdown document:
def format_obsidian_document(date_str: str, digest_content: str, stats: dict, config: Config) -> str:
"""Create Obsidian-formatted document."""
prev_day = (datetime.strptime(date_str, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d")
next_day = (datetime.strptime(date_str, "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%d")
return f"""---
date: {date_str}
type: daily-digest
tags: [team, standup, daily]
contributors: {stats['contributor_count']}
messages: {stats['total_messages']}
channels: {stats['active_channels']}
---
# Team Digest - {date_str}
**📊 Activity Summary**
- {stats['total_messages']} messages across {stats['active_channels']} channels
- {stats['contributor_count']} active team members
- Time range: Last 24 hours
---
{digest_content}
---
**🔗 Links**
- [[{prev_day} - Team Digest|← Previous Day]]
- [[{next_day} - Team Digest|Next Day →]]
---
*Auto-generated at {datetime.now(config.eastern_tz).strftime('%I:%M %p ET')} from Discord*
"""
YAML Frontmatter Structure
The frontmatter contains structured metadata that Obsidian can query:
---
date: 2026-03-04
type: daily-digest
tags: [team, standup, daily]
contributors: 8
messages: 247
channels: 6
---
| Field | Type | Purpose | Example |
|---|
date | String (YYYY-MM-DD) | ISO date of digest | 2026-03-04 |
type | String | Document category | daily-digest |
tags | List | Searchable tags | [team, standup, daily] |
contributors | Integer | Unique human contributors | 8 |
messages | Integer | Total messages analyzed | 247 |
channels | Integer | Active channels | 6 |
Using Frontmatter in Obsidian
Dataview queries:
TABLE contributors, messages, channels
FROM "Daily Digests"
WHERE type = "daily-digest"
SORT date DESC
LIMIT 7
Filter by activity:
LIST
FROM "Daily Digests"
WHERE messages > 100
SORT date DESC
Chart activity trends:
TABLE date as Date, messages as Messages, contributors as Contributors
FROM "Daily Digests"
WHERE date >= date(today) - dur(30 days)
SORT date ASC
Daily Note Links
The bot automatically creates bidirectional links to adjacent days:
**🔗 Links**
- [[2026-03-03 - Team Digest|← Previous Day]]
- [[2026-03-05 - Team Digest|Next Day →]]
Link generation code:
prev_day = (datetime.strptime(date_str, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d")
next_day = (datetime.strptime(date_str, "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%d")
Navigation Benefits
- Sequential browsing: Click through days like a calendar
- Temporal context: See what happened before/after
- Graph view: Visualize daily digest timeline
- Automatic backlinks: Obsidian creates reverse links automatically
How It Works in Obsidian
- Click ”← Previous Day” → Opens
2026-03-03 - Team Digest.md
- Graph view shows chain of all digests
- Backlinks panel shows days that reference current note
- Hover preview displays snippet without opening
Complete Document Example
Here’s a full Obsidian document as generated by the bot:
---
date: 2026-03-04
type: daily-digest
tags: [team, standup, daily]
contributors: 8
messages: 247
channels: 6
---
# Team Digest - 2026-03-04
**📊 Activity Summary**
- 247 messages across 6 channels
- 8 active team members
- Time range: Last 24 hours
---
## Individual Updates
**John Doe**
- Completed the authentication refactor and pushed to PR #234
- Fixed the database migration foreign key constraint issue
- Starting work on API rate limiting implementation
**Jane Smith**
- Reviewed authentication PR, left comments on error handling
- Updated API documentation with new endpoints
- Increased test coverage from 72% to 85%
**Mike Johnson**
- Worked through database migration issues (resolved with Sarah's help)
- Fixed mobile responsive issues on dashboard
- Deployed updates to staging environment
## Upcoming Work
- John: API rate limiting and throttling implementation
- Sarah: Search optimization and indexing improvements
- Mike: Dashboard redesign based on new mockups
## Blockers & Challenges
- Migration foreign key constraint (resolved)
- Waiting on design team for dashboard mockups
- Need to decide on logging framework (discussion ongoing)
## Key Decisions & Ideas
- **Decided**: Moving from MySQL to PostgreSQL for better JSON support
- **Idea**: Implement feature flags for gradual feature rollout
- **Agreement**: Shift to weekly deployment cadence
## Action Items
- [ ] John to document new API endpoints
- [ ] Sarah to schedule architecture review
- [ ] Team to vote on logging framework by Friday
- [ ] Mike to follow up with design team on mockups
---
**🔗 Links**
- [[2026-03-03 - Team Digest|← Previous Day]]
- [[2026-03-05 - Team Digest|Next Day →]]
---
*Auto-generated at 12:00 AM ET from Discord*
File Naming Convention
The bot uses a consistent naming pattern for easy sorting:
filename = f"{output_path}/{date_str} - Team Digest.md"
# Example: Daily Digests/2026-03-04 - Team Digest.md
Naming structure:
- Date prefix:
YYYY-MM-DD (ISO 8601 format)
- Separator:
- (space-dash-space)
- Description:
Team Digest
- Extension:
.md
Why this format:
- Chronological sorting: Files sort by date automatically
- Human-readable: Easy to identify specific days
- Obsidian-friendly: Works with daily notes plugins
- Link-compatible: Can be referenced in wikilinks
- Cross-platform: No special characters that cause issues
Example file listing:
Daily Digests/
├── 2026-03-01 - Team Digest.md
├── 2026-03-02 - Team Digest.md
├── 2026-03-03 - Team Digest.md
├── 2026-03-04 - Team Digest.md
└── 2026-03-05 - Team Digest.md
Saving Digests
The save_digest function handles file creation and optional GitHub sync:
async def save_digest(digest_content: str, date_str: str, stats: dict, config: Config) -> str:
"""Save digest to local directory and push to GitHub."""
output_path = get_output_path(config)
# Initialize git repo if configured
if config.github_repo_url:
init_git_repo(output_path, config)
os.makedirs(output_path, exist_ok=True)
filename = f"{output_path}/{date_str} - Team Digest.md"
obsidian_content = format_obsidian_document(date_str, digest_content, stats, config)
with open(filename, "w", encoding="utf-8") as f:
f.write(obsidian_content)
logger.info(f"Saved digest to {filename}")
# Commit and push to GitHub
if config.github_repo_url:
git_commit_and_push(filename, date_str, config)
return filename
Output path logic:
def get_output_path(config: Config) -> str:
"""Determine the output path for digests."""
if config.dokploy_volume_path and os.path.exists(config.dokploy_volume_path):
logger.info(f"Saving to dokploy volume: {config.dokploy_volume_path}/{config.digests_dir}")
return f"{config.dokploy_volume_path}/{config.digests_dir}"
logger.info(f"Dokploy volume not found, saving to local: {config.digests_dir}")
return config.digests_dir
- Production: Saves to Docker volume (e.g.,
/mnt/digests/Daily Digests)
- Local development: Saves to
./Daily Digests/
Using in Obsidian Vault
Setup Steps
-
Configure output directory in
.env:
# Point to your Obsidian vault
DOKPLOY_VOLUME_PATH="/path/to/your/ObsidianVault"
-
Or use GitHub sync (recommended for teams):
GITHUB_REPO_URL="https://github.com/yourorg/team-digests"
GITHUB_TOKEN="ghp_your_token_here"
-
Open in Obsidian:
- File → Open vault → Select the directory containing
Daily Digests/
- Or add as existing vault if already open
Recommended Plugins
Dataview - Query and visualize digest metadata:
TABLE messages, contributors, channels
FROM "Daily Digests"
WHERE date >= date(today) - dur(7 days)
SORT date DESC
Calendar - Visual calendar view of digests:
- Shows digests on calendar grid
- Click date to open that day’s digest
- See activity density at a glance
Templater - Create custom digest views:
# Weekly Summary
<% tp.date.now("YYYY-[W]WW") %>
<%*
const files = app.vault.getMarkdownFiles()
.filter(f => f.path.includes("Daily Digests"))
.slice(0, 7);
for (let file of files) {
tR += `- [[${file.basename}]]\n`;
}
%>
Graph View (built-in) - Visualize digest timeline:
- Shows daily note links as connected chain
- Identify gaps in digests
- See backlinks and connections
Workflow Examples
Daily standup meeting:
- Open today’s digest in Obsidian
- Review “Individual Updates” section
- Discuss “Blockers & Challenges”
- Assign “Action Items” to team members
Weekly review:
- Use Dataview to list last 7 days
- Compare contributor counts and message volumes
- Identify trends (increasing blockers, declining activity)
Quarterly planning:
- Search for “Key Decisions” across all digests
- Review “Action Items” completion rate
- Analyze team velocity and capacity
GitHub Sync
The bot can automatically commit and push digests to GitHub:
def git_commit_and_push(file_path: str, date_str: str, config: Config) -> bool:
"""Commit and push the digest file to GitHub."""
if not config.github_repo_url or not config.github_token:
logger.warning("GitHub not configured, skipping push")
return False
output_path = os.path.dirname(file_path)
try:
# Configure git user (required for commits)
subprocess.run(
["git", "-C", output_path, "config", "user.name", "Discord Bot"],
check=True,
capture_output=True
)
subprocess.run(
["git", "-C", output_path, "config", "user.email", "bot@discord.local"],
check=True,
capture_output=True
)
# Add the file
subprocess.run(
["git", "-C", output_path, "add", os.path.basename(file_path)],
check=True,
capture_output=True
)
# Commit
commit_message = f"Daily digest for {date_str}"
subprocess.run(
["git", "-C", output_path, "commit", "-m", commit_message],
check=True,
capture_output=True,
text=True
)
# Push with authentication
auth_url = config.github_repo_url.replace(
"https://",
f"https://{config.github_token}@"
)
subprocess.run(
["git", "-C", output_path, "push", auth_url, "main"],
check=True,
capture_output=True,
text=True
)
logger.info(f"Successfully pushed {file_path} to GitHub")
return True
except subprocess.CalledProcessError as e:
logger.error(f"Git operation failed: {e.stderr if hasattr(e, 'stderr') else str(e)}")
return False
Benefits of GitHub sync:
- Team access: Everyone can access digests via GitHub
- Version history: Track changes and modifications
- Backup: Digests are safely stored remotely
- Obsidian Sync alternative: Free sync solution using Git
Setup:
# .env configuration
GITHUB_REPO_URL="https://github.com/yourorg/team-digests"
GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxx"
Commit messages:
Daily digest for 2026-03-04
The activity summary is generated from export statistics:
# From format_obsidian_document function
**📊 Activity Summary**
- {stats['total_messages']} messages across {stats['active_channels']} channels
- {stats['contributor_count']} active team members
- Time range: Last 24 hours
Stats come from calculate_export_stats (see Message Export):
stats = {
"total_messages": 247,
"active_channels": 6,
"contributors": {"John Doe", "Jane Smith", "Mike Johnson", ...},
"contributor_count": 8
}
Each digest includes a generation timestamp:
*Auto-generated at {datetime.now(config.eastern_tz).strftime('%I:%M %p ET')} from Discord*
# Example: Auto-generated at 12:00 AM ET from Discord
Format: 12-hour time with AM/PM and Eastern Time zone
See Timezone Handling for details on time conversion.