14 Commits

Author SHA1 Message Date
53dad27b4f fix: replace remaining OWNER_ID reference with OWNER_IDS 2025-11-02 00:43:34 +01:00
38211f8d54 fix: add logging and error handling for OWNER_ID parsing 2025-11-02 00:25:22 +01:00
a540553e44 feat: support multiple owner IDs via comma-separated OWNER_ID env var
Amp-Thread-ID: https://ampcode.com/threads/T-d9bbd9a4-ce62-4729-b3e5-e83ce72aaf7d
Co-authored-by: Amp <amp@ampcode.com>
2025-11-02 00:20:59 +01:00
0a005fc338 fix: correct Docker networking and missing quote import
Amp-Thread-ID: https://ampcode.com/threads/T-d9bbd9a4-ce62-4729-b3e5-e83ce72aaf7d
Co-authored-by: Amp <amp@ampcode.com>
2025-11-01 22:10:57 +01:00
Wiktor
b82621becb Merge pull request #4 from overspend1/codex/fix-dockerfile-and-compose-for-meme-wrangler-w3lgdz
fix: route compose db connections to postgres service
2025-11-01 22:54:12 +01:00
Wiktor
7e6d46d9c9 Merge branch 'main' into codex/fix-dockerfile-and-compose-for-meme-wrangler-w3lgdz 2025-11-01 22:54:01 +01:00
Wiktor
1cbb4513d7 Update bot.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-01 22:52:16 +01:00
Wiktor
3d81b8d6b8 Update bot.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-01 22:51:58 +01:00
Wiktor
63dc74e8a2 fix: route compose db connections to postgres service 2025-11-01 22:05:21 +01:00
Wiktor
2d6c388a3e Merge pull request #3 from overspend1/codex/fix-dockerfile-and-compose-for-meme-wrangler-dy6rbm
fix: support compose env overrides
2025-11-01 19:58:21 +01:00
Wiktor
93b6cf7a63 Merge branch 'main' into codex/fix-dockerfile-and-compose-for-meme-wrangler-dy6rbm 2025-11-01 19:58:14 +01:00
Wiktor
66c5a5a797 fix: default compose stack to .ENV 2025-11-01 19:55:27 +01:00
Wiktor
160e792e8f fix: support compose env overrides 2025-11-01 19:45:49 +01:00
Wiktor
b1cd5dfeb2 Merge pull request #2 from overspend1/codex/fix-dockerfile-and-compose-for-meme-wrangler
fix: make compose env file optional
2025-11-01 19:27:56 +01:00
10 changed files with 143 additions and 39 deletions

12
.ENV.example Normal file
View File

@@ -0,0 +1,12 @@
# Sample environment for Meme Wrangler Docker stack.
# Copy to `.ENV` and adjust secrets before deploying.
TELEGRAM_BOT_TOKEN=
OWNER_ID=0
CHANNEL_ID=
POSTGRES_DB=meme_wrangler
POSTGRES_USER=meme
POSTGRES_PASSWORD=meme
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
MEMEBOT_BACKUP_DIR=/app/backups
# MEMEBOT_BACKUP_PASSWORD_HASH=

View File

@@ -5,3 +5,4 @@ __pycache__
backups backups
.env .env
.env.* .env.*
.ENV

View File

@@ -2,6 +2,7 @@
# Copy this file to .env and fill in your actual values # Copy this file to .env and fill in your actual values
TELEGRAM_BOT_TOKEN=your_bot_token_here TELEGRAM_BOT_TOKEN=your_bot_token_here
# Comma-separated list of owner Telegram user IDs (e.g., 123456789,987654321)
OWNER_ID=your_telegram_user_id_here OWNER_ID=your_telegram_user_id_here
CHANNEL_ID=@your_channel_or_id_here CHANNEL_ID=@your_channel_or_id_here
# PostgreSQL connection string (format: postgresql://user:password@host:port/database) # PostgreSQL connection string (format: postgresql://user:password@host:port/database)

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@
__pycache__/ __pycache__/
*.pyc *.pyc
memes.db memes.db
.env .env
.ENV

View File

@@ -29,6 +29,6 @@
- Link issues or TODOs, flag breaking changes in bold, and include screenshots only when UI-facing Telegram copy changes. - Link issues or TODOs, flag breaking changes in bold, and include screenshots only when UI-facing Telegram copy changes.
## Deployment & Secret Tips ## Deployment & Secret Tips
- Keep `.env` out of version control; copy from `.env.example` and set `TELEGRAM_BOT_TOKEN`, `OWNER_ID`, `CHANNEL_ID`, `POSTGRES_*` credentials, and `DATABASE_URL` for local runs. - Keep `.ENV` (or `.env`) out of version control; copy from `.ENV.example` and set `TELEGRAM_BOT_TOKEN`, `OWNER_ID`, `CHANNEL_ID`, `POSTGRES_*` credentials, and `DATABASE_URL` for local runs.
- Only override `MEMEBOT_BACKUP_PASSWORD_HASH` if you plan to replace the baked-in SHA-256 hash for backup commands. - Only override `MEMEBOT_BACKUP_PASSWORD_HASH` if you plan to replace the baked-in SHA-256 hash for backup commands.
- Docker workflows mount backups in `./backups/` and keep the PostgreSQL cluster in the `pgdata` volume; prune carefully when resetting schedules. New memes automatically generate a fresh backup file in that directory. - Docker workflows mount backups in `./backups/` and keep the PostgreSQL cluster in the `pgdata` volume; prune carefully when resetting schedules. New memes automatically generate a fresh backup file in that directory.

View File

@@ -37,8 +37,8 @@ sudo usermod -aG docker $USER
Copy the example file and edit it: Copy the example file and edit it:
```bash ```bash
cp .env.example .env cp .ENV.example .ENV
nano .env # or use any text editor nano .ENV # or use any text editor
``` ```
Fill in your actual values: Fill in your actual values:
@@ -49,16 +49,22 @@ CHANNEL_ID=@yourchannel
POSTGRES_DB=meme_wrangler POSTGRES_DB=meme_wrangler
POSTGRES_USER=meme POSTGRES_USER=meme
POSTGRES_PASSWORD=meme POSTGRES_PASSWORD=meme
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
# Optional: adjust which env file Compose should read (defaults to .ENV)
# COMPOSE_ENV_FILE=staging.env
# Optional: hash (SHA-256) for replacing the baked-in backup secret # Optional: hash (SHA-256) for replacing the baked-in backup secret
# MEMEBOT_BACKUP_PASSWORD_HASH=<your_sha256_hash> # MEMEBOT_BACKUP_PASSWORD_HASH=<your_sha256_hash>
# Optional: adjust DATABASE_URL for non-compose workflows # Optional: adjust DATABASE_URL for non-compose workflows
# DATABASE_URL=postgresql://meme:meme@postgres:5432/meme_wrangler # DATABASE_URL=postgresql://meme:meme@postgres:5432/meme_wrangler
``` ```
Leave `POSTGRES_HOST=postgres` when running through Docker Compose or Portainer; it's the internal service name that the bot rewrites into any localhost-style URLs so the container connects to the bundled PostgreSQL instance. Override it only if your database lives elsewhere.
### 2. Build and Run with Docker Compose (Easiest) ### 2. Build and Run with Docker Compose (Easiest)
```bash ```bash
# Build and start in background # Build and start in background (stack reads .ENV automatically)
docker-compose up -d docker-compose up -d
# View logs # View logs
@@ -73,6 +79,10 @@ docker-compose restart
That's it! Your bot is now running in Docker! 🎉 That's it! Your bot is now running in Docker! 🎉
#### Deploying with Portainer Stacks
When launching the stack from Portainer, upload your `.ENV` file through the **Environment variables** tab—Portainer saves it beside the stack as `/data/compose/<stack-id>/.ENV`, which matches the default expected by `docker-compose.yml`. Only set `COMPOSE_ENV_FILE` if you deliberately use a different name.
## Alternative: Using Docker Commands Directly ## Alternative: Using Docker Commands Directly
### Build the Image ### Build the Image
@@ -152,9 +162,9 @@ ssh -i /path/to/ssh_key username@server_ip
# Navigate to bot directory # Navigate to bot directory
cd ~/meme-wrangler cd ~/meme-wrangler
# Create .env file # Create .ENV file
cp .env.example .env cp .ENV.example .ENV
nano .env # Fill in your credentials nano .ENV # Fill in your credentials
# Build and run # Build and run
docker-compose up -d docker-compose up -d
@@ -308,8 +318,8 @@ scp -i /path/to/key -r \
ssh -i /path/to/key username@server_ip ssh -i /path/to/key username@server_ip
cd ~/memebot cd ~/memebot
# Create .env file # Create .ENV file
nano .env # Add your credentials nano .ENV # Add your credentials
# Run with Docker Compose # Run with Docker Compose
docker-compose up -d docker-compose up -d

View File

@@ -10,10 +10,10 @@ The easiest way to run the bot is using Docker:
1. **Set up environment variables:** 1. **Set up environment variables:**
```bash ```bash
cp .env.example .env cp .ENV.example .ENV
nano .env # Edit with your bot credentials nano .ENV # Edit with your bot credentials
``` ```
The compose stack expects PostgreSQL credentials, so populate `POSTGRES_DB`, `POSTGRES_USER`, and `POSTGRES_PASSWORD` (defaults provided). Backups are protected by a built-in SHA-256 hash; optionally define `MEMEBOT_BACKUP_PASSWORD_HASH` to replace it. The compose file now looks for `.ENV` by default so Portainer and other orchestrators can supply secrets without additional flags. Copy `.ENV.example` to `.ENV`, then populate `TELEGRAM_BOT_TOKEN`, `OWNER_ID`, `CHANNEL_ID`, and the Postgres settings. Leave `POSTGRES_HOST=postgres` (and `POSTGRES_PORT=5432` unless your database listens elsewhere) when you run inside Docker. The bot rewrites any localhost URLs with that host so the container connects to the bundled PostgreSQL service instead of looping back on itself. You can still point the stack at a different file by exporting `COMPOSE_ENV_FILE` (e.g. `COMPOSE_ENV_FILE=staging.env docker compose up -d`). Backups are protected by a built-in SHA-256 hash; optionally define `MEMEBOT_BACKUP_PASSWORD_HASH` to replace it.
2. **Run with Docker Compose:** 2. **Run with Docker Compose:**
```bash ```bash
@@ -25,6 +25,8 @@ The easiest way to run the bot is using Docker:
docker-compose logs -f docker-compose logs -f
``` ```
Deploying via Portainer? Upload your `.ENV` file under **Environment variables**—Portainer stores it beside the stack so the compose file picks it up automatically. Override `COMPOSE_ENV_FILE` only when you use a differently named file.
4. **Stop the bot:** 4. **Stop the bot:**
```bash ```bash
docker-compose down docker-compose down
@@ -51,6 +53,9 @@ export TELEGRAM_BOT_TOKEN=123:ABC
export OWNER_ID=123456789 export OWNER_ID=123456789
export CHANNEL_ID=@yourchannel # or -1001234567890 export CHANNEL_ID=@yourchannel # or -1001234567890
export DATABASE_URL=postgresql://meme:meme@localhost:5432/meme_wrangler export DATABASE_URL=postgresql://meme:meme@localhost:5432/meme_wrangler
# Optional pieces that mirror the Docker variables
# export POSTGRES_HOST=localhost
# export POSTGRES_PORT=5432
# Optional: where JSON backups are written # Optional: where JSON backups are written
# export MEMEBOT_BACKUP_DIR=/path/to/backups # export MEMEBOT_BACKUP_DIR=/path/to/backups
# export MEMEBOT_BACKUP_PASSWORD_HASH=<sha256 hash of your backup secret> # export MEMEBOT_BACKUP_PASSWORD_HASH=<sha256 hash of your backup secret>

92
bot.py
View File

@@ -10,6 +10,7 @@ import io
import json import json
import logging import logging
import os import os
from urllib.parse import urlparse, urlunparse
from datetime import datetime, time, timedelta from datetime import datetime, time, timedelta
from pathlib import Path from pathlib import Path
from types import SimpleNamespace from types import SimpleNamespace
@@ -89,9 +90,74 @@ def _ensure_ist(dt: datetime) -> datetime:
return _ist_localize(dt) return _ist_localize(dt)
return dt.astimezone(IST) return dt.astimezone(IST)
DATABASE_URL = os.environ.get("DATABASE_URL") or os.environ.get("MEMEBOT_DB") def _build_database_url() -> Optional[str]:
"""Derive the database URL from explicit env vars or component pieces."""
raw_url = os.environ.get("DATABASE_URL") or os.environ.get("MEMEBOT_DB")
if not raw_url:
user = os.environ.get("POSTGRES_USER")
password = os.environ.get("POSTGRES_PASSWORD")
db_name = os.environ.get("POSTGRES_DB")
host = os.environ.get("POSTGRES_HOST", "localhost")
port = os.environ.get("POSTGRES_PORT", "5432")
if user and password and db_name:
raw_url = (
f"postgresql://{quote(user, safe='')}:{quote(password, safe='')}"
f"@{host}:{port}/{db_name}"
)
if raw_url:
return _normalize_database_url(raw_url)
return None
def _normalize_database_url(url: str) -> str:
"""Replace localhost hosts with the configured Postgres host for container runs."""
host_override = os.environ.get("POSTGRES_HOST")
if not host_override:
return url
host_override = host_override.strip()
if not host_override or host_override in {"localhost", "127.0.0.1", "::1"}:
return url
parsed = urlparse(url)
if parsed.hostname not in {"localhost", "127.0.0.1", "::1"}:
return url
if "@" in parsed.netloc:
auth_prefix, _, _ = parsed.netloc.rpartition("@")
auth_segment = f"{auth_prefix}@"
else:
auth_segment = ""
if parsed.port is not None:
port_fragment = f":{parsed.port}"
else:
port_env = os.environ.get("POSTGRES_PORT")
port_fragment = f":{port_env}" if port_env else ""
target_host = host_override
if ":" in target_host and not target_host.startswith("["):
target_host = f"[{target_host}]"
new_netloc = f"{auth_segment}{target_host}{port_fragment}"
rebuilt = parsed._replace(netloc=new_netloc)
return urlunparse(rebuilt)
DATABASE_URL = _build_database_url()
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN") BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN")
OWNER_ID = int(os.environ.get("OWNER_ID", "0"))
# Support multiple owner IDs (comma-separated)
_owner_ids_raw = os.environ.get("OWNER_ID", "0")
try:
OWNER_IDS = set(int(oid.strip()) for oid in _owner_ids_raw.split(",") if oid.strip())
logger.info(f"Configured owner IDs: {OWNER_IDS}")
except ValueError as e:
logger.error(f"Failed to parse OWNER_ID env var '{_owner_ids_raw}': {e}")
OWNER_IDS = {0}
CHANNEL_ID = os.environ.get("CHANNEL_ID") # @channelusername or -100<id> CHANNEL_ID = os.environ.get("CHANNEL_ID") # @channelusername or -100<id>
BACKUP_DIR = Path(os.environ.get("MEMEBOT_BACKUP_DIR", "backups")) BACKUP_DIR = Path(os.environ.get("MEMEBOT_BACKUP_DIR", "backups"))
_HARDCODED_BACKUP_PASSWORD_HASH = "16c5b5ddf1b27f16ad5f801bb83595d00e666cc53085e53a4b1e67b715016251" _HARDCODED_BACKUP_PASSWORD_HASH = "16c5b5ddf1b27f16ad5f801bb83595d00e666cc53085e53a4b1e67b715016251"
@@ -254,7 +320,7 @@ async def pop_due_memes_and_post(context: ContextTypes.DEFAULT_TYPE):
posting_log.pop(0) posting_log.pop(0)
async def scheduled(update: Update, context: ContextTypes.DEFAULT_TYPE): async def scheduled(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
@@ -340,7 +406,7 @@ async def scheduled(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(caption) await update.message.reply_text(caption)
async def unschedule(update: Update, context: ContextTypes.DEFAULT_TYPE): async def unschedule(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
if not context.args or not all(arg.isdigit() for arg in context.args): if not context.args or not all(arg.isdigit() for arg in context.args):
@@ -359,7 +425,7 @@ async def unschedule(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def preview(update: Update, context: ContextTypes.DEFAULT_TYPE): async def preview(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Preview a scheduled meme by id. Tries direct send, then downloads and reuploads as a document if needed.""" """Preview a scheduled meme by id. Tries direct send, then downloads and reuploads as a document if needed."""
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
if not context.args or not context.args[0].isdigit(): if not context.args or not context.args[0].isdigit():
@@ -420,7 +486,7 @@ async def preview(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def logcmd(update: Update, context: ContextTypes.DEFAULT_TYPE): async def logcmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
if not posting_log: if not posting_log:
@@ -431,7 +497,7 @@ async def logcmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def backup(update: Update, context: ContextTypes.DEFAULT_TYPE): async def backup(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
if not _verify_backup_password(context.args): if not _verify_backup_password(context.args):
@@ -500,7 +566,7 @@ async def create_backup(send_document_to: Optional[int] = None, bot: Optional[An
async def restore(update: Update, context: ContextTypes.DEFAULT_TYPE): async def restore(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
if not _verify_backup_password(context.args): if not _verify_backup_password(context.args):
@@ -627,7 +693,7 @@ async def helpcmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
<i>Example:</i> <code>/scheduleat ids: 5-10 2025-10-19</code> <i>Example:</i> <code>/scheduleat ids: 5-10 2025-10-19</code>
<b>Notes:</b> <b>Notes:</b>
• <b>Only the owner</b> (set by OWNER_ID) can use admin commands. • <b>Only owners</b> (set by OWNER_ID) can use admin commands.
• All times are in <b>IST (Asia/Kolkata)</b>. • All times are in <b>IST (Asia/Kolkata)</b>.
• Meme IDs are shown in <b>/scheduled</b> previews. • Meme IDs are shown in <b>/scheduled</b> previews.
• Use <b>/preview</b> to check a meme before posting. • Use <b>/preview</b> to check a meme before posting.
@@ -640,7 +706,7 @@ async def helpcmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def handle_media(update: Update, context: ContextTypes.DEFAULT_TYPE): async def handle_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
msg: Message = update.message msg: Message = update.message
user_id = msg.from_user.id user_id = msg.from_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await msg.reply_text("Sorry, only the owner can send memes to schedule.") await msg.reply_text("Sorry, only the owner can send memes to schedule.")
return return
@@ -681,7 +747,7 @@ async def handle_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def postnow(update: Update, context: ContextTypes.DEFAULT_TYPE): async def postnow(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
@@ -725,7 +791,7 @@ import re
async def scheduleat(update: Update, context: ContextTypes.DEFAULT_TYPE): async def scheduleat(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id user_id = update.effective_user.id
if user_id != OWNER_ID: if user_id not in OWNER_IDS:
await update.message.reply_text("Only the owner can use this command.") await update.message.reply_text("Only the owner can use this command.")
return return
if not context.args or len(context.args) < 2: if not context.args or len(context.args) < 2:
@@ -793,7 +859,7 @@ async def scheduleat(update: Update, context: ContextTypes.DEFAULT_TYPE):
def main(): def main():
if not BOT_TOKEN: if not BOT_TOKEN:
raise SystemExit("Please set TELEGRAM_BOT_TOKEN environment variable") raise SystemExit("Please set TELEGRAM_BOT_TOKEN environment variable")
if not OWNER_ID or OWNER_ID == 0: if not OWNER_IDS or 0 in OWNER_IDS:
raise SystemExit("Please set OWNER_ID environment variable to your Telegram user id") raise SystemExit("Please set OWNER_ID environment variable to your Telegram user id")
if not CHANNEL_ID: if not CHANNEL_ID:
raise SystemExit("Please set CHANNEL_ID to target channel (username or id)") raise SystemExit("Please set CHANNEL_ID to target channel (username or id)")

View File

@@ -26,16 +26,20 @@ if [ -z "$SERVER" ]; then
exit 1 exit 1
fi fi
echo "Step 1: Checking if .env file exists..." ENV_FILE=".ENV"
if [ ! -f ".env" ]; then echo "Step 1: Checking for environment file (.ENV or .env)..."
echo "Error: .env file not found!" if [ -f "$ENV_FILE" ]; then
echo "Please create .env file with your credentials:" echo "✓ .ENV file found"
echo " cp .env.example .env" elif [ -f ".env" ]; then
echo " nano .env" ENV_FILE=".env"
echo "✓ .env file found"
else
echo "Error: No environment file found!"
echo "Please create one with your credentials:"
echo " cp .ENV.example .ENV"
echo " nano .ENV"
exit 1 exit 1
fi fi
echo "✓ .env file found"
echo "" echo ""
echo "Step 2: Uploading files to server..." echo "Step 2: Uploading files to server..."
@@ -45,7 +49,7 @@ scp -i "$SSH_KEY" \
docker-compose.yml \ docker-compose.yml \
bot.py \ bot.py \
requirements.txt \ requirements.txt \
.env \ "$ENV_FILE" \
"$SERVER":~/meme-wrangler/ "$SERVER":~/meme-wrangler/
echo "✓ Files uploaded" echo "✓ Files uploaded"

View File

@@ -1,10 +1,12 @@
x-env-file: &compose_env_file ${COMPOSE_ENV_FILE:-.env}
services: services:
postgres: postgres:
image: postgres:15 image: postgres:15
container_name: meme-wrangler-db container_name: meme-wrangler-db
restart: unless-stopped restart: unless-stopped
env_file: env_file:
- .env - *compose_env_file
environment: environment:
- POSTGRES_DB=${POSTGRES_DB:-meme_wrangler} - POSTGRES_DB=${POSTGRES_DB:-meme_wrangler}
- POSTGRES_USER=${POSTGRES_USER:-meme} - POSTGRES_USER=${POSTGRES_USER:-meme}
@@ -26,7 +28,7 @@ services:
postgres: postgres:
condition: service_healthy condition: service_healthy
env_file: env_file:
- .env - *compose_env_file
environment: environment:
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-} - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-}
- OWNER_ID=${OWNER_ID:-} - OWNER_ID=${OWNER_ID:-}
@@ -34,8 +36,10 @@ services:
- POSTGRES_DB=${POSTGRES_DB:-meme_wrangler} - POSTGRES_DB=${POSTGRES_DB:-meme_wrangler}
- POSTGRES_USER=${POSTGRES_USER:-meme} - POSTGRES_USER=${POSTGRES_USER:-meme}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-meme} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-meme}
- DATABASE_URL=postgresql://${POSTGRES_USER:-meme}:${POSTGRES_PASSWORD:-meme}@postgres:5432/${POSTGRES_DB:-meme_wrangler} - POSTGRES_HOST=${POSTGRES_HOST:-postgres}
- MEMEBOT_BACKUP_DIR=/app/backups - POSTGRES_PORT=${POSTGRES_PORT:-5432}
- DATABASE_URL=${DATABASE_URL:-postgresql://${POSTGRES_USER:-meme}:${POSTGRES_PASSWORD:-meme}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-meme_wrangler}}
- MEMEBOT_BACKUP_DIR=${MEMEBOT_BACKUP_DIR:-/app/backups}
volumes: volumes:
- ./backups:/app/backups - ./backups:/app/backups
logging: logging: