diff --git a/.gitignore b/.gitignore index 255709d..881a66b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ __pycache__ database.json -database.bak \ No newline at end of file +database.bak +.env +dbhost/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9e8689d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +# Use latest alpine-derived Python base image +FROM python:3-alpine + +# Move to app directory + +# Install requirements +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +# Copy source to app directory +COPY . . + +# Make sure run script is executable +RUN chmod +rxxx ./run.sh + +CMD sh run.sh \ No newline at end of file diff --git a/README.md b/README.md index 9769fe3..0d338fc 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,7 @@ In the settings.py you can also disable posting to twitter or mastodon if you on The file settings.py now also allows (mis)using blueskys language function by designating a language that when set can be used to decide if a specific post should or should not be crossposted. More info can be found in the file. +## Running with Docker +The included Dockerfile and docker-compose file can be used to run the service in a docker container. Configuration options can be set in the docker-compose file, added to an .env file (see env.example) or injected as environment variables in some other way. An additional configuration option, RUN_INTERVAL, is provided to set the interval in seconds for which to check for new posts. + Bluesky Crossposter™©® developed by denvitadrogen diff --git a/auth.py b/auth.py index 91ed868..2da5cf9 100644 --- a/auth.py +++ b/auth.py @@ -1,3 +1,5 @@ +import os + # All necessary tokens, passwords, etc. # Your bluesky handle should include your instance, so for example handle.bsky.social if you are on the main one. bsky_handle = "" @@ -13,4 +15,14 @@ MASTODON_TOKEN = "" TWITTER_APP_KEY = "" TWITTER_APP_SECRET = "" TWITTER_ACCESS_TOKEN = "" -TWITTER_ACCESS_TOKEN_SECRET = "" \ No newline at end of file +TWITTER_ACCESS_TOKEN_SECRET = "" + +# Override settings with environment variables if they exist +bsky_handle = os.environ.get('BSKY_HANDLE') if os.environ.get('BSKY_HANDLE') else bsky_handle +bsky_password = os.environ.get('BSKY_PASSWORD') if os.environ.get('BSKY_PASSWORD') else bsky_password +MASTODON_INSTANCE = os.environ.get('MASTODON_INSTANCE') if os.environ.get('MASTODON_INSTANCE') else MASTODON_INSTANCE +MASTODON_TOKEN = os.environ.get('MASTODON_TOKEN') if os.environ.get('MASTODON_TOKEN') else MASTODON_TOKEN +TWITTER_APP_KEY = os.environ.get('TWITTER_APP_KEY') if os.environ.get('TWITTER_APP_KEY') else TWITTER_APP_KEY +TWITTER_APP_SECRET = os.environ.get('TWITTER_APP_SECRET') if os.environ.get('TWITTER_APP_SECRET') else TWITTER_APP_SECRET +TWITTER_ACCESS_TOKEN = os.environ.get('TWITTER_ACCESS_TOKEN') if os.environ.get('TWITTER_ACCESS_TOKEN') else TWITTER_ACCESS_TOKEN +TWITTER_ACCESS_TOKEN_SECRET = os.environ.get('TWITTER_ACCESS_TOKEN_SECRET') if os.environ.get('TWITTER_ACCESS_TOKEN_SECRET') else TWITTER_ACCESS_TOKEN_SECRET \ No newline at end of file diff --git a/db/.gitkeep b/db/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..799cb13 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.8' +services: + crossposter: + build: . + environment: + BSKY_HANDLE: + BSKY_PASSWORD: + MASTODON_INSTANCE: + MASTODON_TOKEN: + TWITTER_APP_KEY: + TWITTER_APP_SECRET: + TWITTER_ACCESS_TOKEN: + TWITTER_ACCESS_TOKEN_SECRET: + TWITTER_CROSSPOSTING: + MASTODON_CROSSPOSTING: + LOGGING: + POST_DEFAULT: + MASTODON_LANG: + TWITTER_LANG: + MAX_RETRIES: + RUN_INTERVAL: + POST_TIME_LIMIT: + volumes: + - ./dbhost:/db \ No newline at end of file diff --git a/env.example b/env.example new file mode 100644 index 0000000..3c1a927 --- /dev/null +++ b/env.example @@ -0,0 +1,17 @@ +BSKY_HANDLE= +BSKY_PASSWORD= +MASTODON_INSTANCE= +MASTODON_TOKEN= +TWITTER_APP_KEY= +TWITTER_APP_SECRET= +TWITTER_ACCESS_TOKEN= +TWITTER_ACCESS_TOKEN_SECRET= +TWITTER_CROSSPOSTING= +MASTODON_CROSSPOSTING= +LOGGING= +POST_DEFAULT= +MASTODON_LANG= +TWITTER_LANG= +MAX_RETRIES= +RUN_INTERVAL= +POST_TIME_LIMIT= \ No newline at end of file diff --git a/paths.py b/paths.py index bc6d626..1298304 100644 --- a/paths.py +++ b/paths.py @@ -5,9 +5,9 @@ basePath = "/" # Path to the database file. If you want it somewhere other than directly in the base path you can # either write the entire path manually, or just add the rest of the path on top of the basePath. -databasePath = basePath + "database.json" +databasePath = basePath + "db/" + "database.json" # Path to backup of database. -backupPath = basePath + "database.bak" +backupPath = basePath + "db/" + "database.bak" # Path for storing logs logPath = basePath + "logs/" # Path to folder for temporary storage of images diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..5966824 --- /dev/null +++ b/run.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Run once per hour if nothing else has been specified in environment variables +while :; do + python crosspost.py + sleep ${RUN_INTERVAL:-3600} +done \ No newline at end of file diff --git a/settings.py b/settings.py index 5b0eb6e..77bec07 100644 --- a/settings.py +++ b/settings.py @@ -1,3 +1,5 @@ +import os + # Enables/disables crossposting to twitter and mastodon # Accepted values: True, False Twitter = True @@ -26,3 +28,13 @@ maxRetries = 5 # Sets max time limit (in hours) for fetching posts. If no database exists, all posts within this time # period will be posted. postTimeLimit = 12 + +# Override settings with environment variables if they exist +Twitter = os.environ.get('TWITTER_CROSSPOSTING').lower() == 'true' if os.environ.get('TWITTER_CROSSPOSTING') else Twitter +Mastodon = os.environ.get('MASTODON_CROSSPOSTING').lower() == 'true' if os.environ.get('MASTODON_CROSSPOSTING') else Mastodon +Logging = os.environ.get('LOGGING').lower() == 'true' if os.environ.get('LOGGING') else Logging +postDefault = os.environ.get('POST_DEFAULT').lower() == 'true' if os.environ.get('POST_DEFAULT') else postDefault +mastodonLang = os.environ.get('MASTODON_LANG') if os.environ.get('MASTODON_LANG') else mastodonLang +twitterLang = os.environ.get('TWITTER_LANG') if os.environ.get('TWITTER_LANG') else twitterLang +maxRetries = int(os.environ.get('MAX_RETRIES')) if os.environ.get('MAX_RETRIES') else maxRetries +postTimeLimit = int(os.environ.get('POST_TIME_LIMIT')) if os.environ.get('POST_TIME_LIMIT') else postTimeLimit