Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Git files
.git
.gitignore
.github

# Documentation
README.md
CLAUDE.md
LICENSE
CHANGELOG.md
*.md

# Development files
*.pyc
__pycache__
.pytest_cache
.coverage
htmlcov
.env
.env.*

# IDE files
.vscode
.idea
*.swp
*.swo
*.swn
.DS_Store

# Local data and logs
data/
logs/
*.db
*.log

# Config files (except template)
config.json

# Test files
test_*.py
tests/
debug_*.py

# Build artifacts
build/
dist/
*.egg-info/

# Systemd files
systemd/
*.service

# Docker files (not needed in build context)
docker-compose.yml
docker-compose.*.yml
Dockerfile.*
55 changes: 55 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

# Python files
[*.py]
indent_style = space
indent_size = 4

# JSON files
[*.json]
indent_style = space
indent_size = 2

# YAML files
[*.{yml,yaml}]
indent_style = space
indent_size = 2

# Markdown files
[*.md]
trim_trailing_whitespace = false
max_line_length = off

# Shell scripts
[*.sh]
indent_style = space
indent_size = 2

# Dockerfile
[Dockerfile*]
indent_style = space
indent_size = 2

# Docker Compose
[docker-compose*.yml]
indent_style = space
indent_size = 2

# Makefile
[Makefile]
indent_style = tab

# Configuration files
[*.{cfg,ini,toml}]
indent_style = space
indent_size = 2
121 changes: 121 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Docker Build and Publish

on:
push:
branches:
- main
- develop
- 'feature/docker-*'
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
inputs:
push:
description: 'Push images to registry'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'

env:
REGISTRY: ghcr.io
IMAGE_NAME: bakerboy448/redditmodlog

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=sha
labels: |
org.opencontainers.image.title=Reddit ModLog Wiki Publisher
org.opencontainers.image.description=Automated Reddit moderation log publisher to wiki pages
org.opencontainers.image.vendor=bakerboy448
org.opencontainers.image.licenses=GPL-3.0

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' || github.event.inputs.push == 'true' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
VERSION=${{ steps.meta.outputs.version }}

- name: Generate SBOM
if: github.event_name != 'pull_request'
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
format: spdx-json
output-file: sbom.spdx.json

- name: Upload SBOM
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json

security-scan:
needs: build
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
permissions:
contents: read
security-events: write

steps:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
format: 'sarif'
output: 'trivy-results.sarif'

- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
config.json
*.config.json
config.*.json
/etc/redditmodlog/*.json
!config_template.json
!*.json.example

# Database files
*.db
Expand Down Expand Up @@ -38,7 +41,13 @@ venv/
ENV/
env/
.venv

# Environment files with credentials
.env
.env.*
*.env
!example.env
!*.env.example

# IDE
.vscode/
Expand Down
57 changes: 57 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- id: debug-statements
- id: check-executables-have-shebangs

- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
language_version: python3
args: [--line-length=180]

- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
args: [--max-line-length=180, --extend-ignore=E203,W503]

- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: [--profile=black]

- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
exclude: |
(?x)^(
config_template\.json|
\.secrets\.baseline
)$

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.4.1
hooks:
- id: mypy
additional_dependencies: [types-requests]
args: [--ignore-missing-imports]

- repo: local
hooks:
- id: reddit-config-check
name: Check Reddit config safety
entry: python -c "import json; config = json.load(open('config.json')) if __import__('os').path.exists('config.json') else {}; exit(1) if not config.get('anonymize_moderators', True) else exit(0)"
language: system
files: config\.json$
pass_filenames: false
18 changes: 9 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ sqlite3 modlog.db "SELECT action_id, action_type, moderator, removal_reason, sub
# View actions by subreddit
sqlite3 modlog.db "SELECT action_type, moderator, target_author, removal_reason FROM processed_actions WHERE subreddit = 'usenet' ORDER BY created_at DESC LIMIT 5;"

# Track content lifecycle by target ID
# Track content lifecycle by target ID
sqlite3 modlog.db "SELECT target_id, action_type, moderator, removal_reason, datetime(created_at, 'unixepoch') FROM processed_actions WHERE target_id LIKE '%1mkz4jm%' ORDER BY created_at;"

# Manual cleanup of old entries
Expand All @@ -62,7 +62,7 @@ sqlite3 modlog.db "DELETE FROM processed_actions WHERE created_at < date('now',

The application supports multiple configuration methods with the following priority (highest to lowest):
1. **Command line arguments** (highest priority)
2. **Environment variables** (override config file)
2. **Environment variables** (override config file)
3. **JSON config file** (base configuration)

### Environment Variables
Expand All @@ -71,7 +71,7 @@ All configuration options can be set via environment variables:

#### Reddit Credentials
- `REDDIT_CLIENT_ID`: Reddit app client ID
- `REDDIT_CLIENT_SECRET`: Reddit app client secret
- `REDDIT_CLIENT_SECRET`: Reddit app client secret
- `REDDIT_USERNAME`: Reddit bot username
- `REDDIT_PASSWORD`: Reddit bot password

Expand Down Expand Up @@ -132,7 +132,7 @@ python modlog_wiki_publisher.py --debug --batch-size 25 # CLI takes priority

### Display Options
- `anonymize_moderators`: **REQUIRED** to be `true` for security (default: true)
- `true` (ENFORCED): Shows "AutoModerator", "Reddit", or "HumanModerator"
- `true` (ENFORCED): Shows "AutoModerator", "Reddit", or "HumanModerator"
- `false`: **BLOCKED** - Would expose moderator identities publicly

**SECURITY NOTE**: Setting `anonymize_moderators=false` is permanently disabled to protect moderator privacy. The application will refuse to start if this is attempted.
Expand All @@ -143,7 +143,7 @@ The application uses configurable action type variables for flexibility:

#### Default Configuration
- **REMOVAL_ACTIONS**: `removelink`, `removecomment`, `spamlink`, `spamcomment`
- **APPROVAL_ACTIONS**: `approvelink`, `approvecomment`
- **APPROVAL_ACTIONS**: `approvelink`, `approvecomment`
- **REASON_ACTIONS**: `addremovalreason`
- **DEFAULT_WIKI_ACTIONS**: All above combined

Expand Down Expand Up @@ -184,7 +184,7 @@ Use `--test` flag to verify configuration and Reddit API connectivity without ma
## Content Link Guidelines

**CRITICAL**: Content links in the modlog should NEVER point to user profiles (`/u/username`). Links should only point to:
- Actual removed posts (`/comments/postid/`)
- Actual removed posts (`/comments/postid/`)
- Actual removed comments (`/comments/postid/_/commentid/`)
- No link at all if no actual content is available

Expand Down Expand Up @@ -231,14 +231,14 @@ User profile links are a privacy concern and not useful for modlog purposes.

### Content Linking and Display
- ✅ Content links point to actual Reddit posts/comments, never user profiles for privacy
- ✅ Fixed target authors showing as [deleted] - now displays actual usernames
- ✅ Fixed target authors showing as [deleted] - now displays actual usernames
- ✅ Proper content titles extracted from Reddit API data
- ✅ AutoModerator displays as "AutoModerator" (not anonymized)
- ✅ Configurable anonymization for human moderators

### Data Integrity
- ✅ Pipe character escaping for markdown table compatibility
- ✅ Robust error handling for mixed subreddit scenarios
- ✅ Robust error handling for mixed subreddit scenarios
- ✅ Database schema at version 5 with all required columns
- ✅ Consistent Reddit API field usage (action.details vs action.description)

Expand Down Expand Up @@ -266,4 +266,4 @@ User profile links are a privacy concern and not useful for modlog purposes.
- **401 errors**: Check app type is "script" and verify client_id/client_secret
- **Wiki permission denied**: Ensure bot has moderator or wiki contributor access
- **Rate limiting**: Increase `--interval` and/or reduce `--batch-size`
- **Module not found**: Always use `/opt/.venv/redditbot/bin/python` instead of system python
- **Module not found**: Always use `/opt/.venv/redditbot/bin/python` instead of system python
Loading
Loading