Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Mocker

A Slack app that lets you mock your friends. Features real-time reactions, reputation tracking, game modes (Muzzle, Backfire, Counter, etc.), AI-powered summaries, and a web-based search interface for message history with team-scoped access control.

Architecture

This project is organized as a npm monorepo with the following structure:

mocker/
├── packages/
│ ├── backend/ # @mocker/backend - Express API server
│ │ # - Slack bot integration
│ │ # - REST APIs for Slack commands and events
│ │ # - Search endpoint (team-scoped, requires OAuth token)
│ │ # - Slack OAuth flow (/auth/slack, /auth/slack/callback)
│ │ # - Scheduled jobs (fun-fact, pricing, event-alert)
│ │
│ └── frontend/ # @mocker/frontend - React + Vite
│ # - Message search UI
│ # - OAuth login flow
│ # - Requires session token in URL fragment
│
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
└── eslint.config.js # Root ESLint config (flat config)

Getting Started

Prerequisites

  • Node.js 24 LTS (>=24.14.1, for backend and frontend development)
  • MySQL 5.7+ (for storing messages, users, game state)
  • Slack workspace (for bot integration)
  • Ngrok (optional, for local tunneling during development)

1. Set Up Slack App

  1. Create a Slack workspace for development: https://slack.com/get-started#/create
  2. Go to https://api.slack.com/apps and create a new app in your workspace.
  3. Configure the app with the following settings:

Slash Commands

Add these slash commands with their request URLs:

  • /mock<backend-url>/mock
  • /define<backend-url>/define
  • /muzzle<backend-url>/muzzle
  • /muzzlestats<backend-url>/muzzle/stats
  • /confess<backend-url>/confess
  • /list<backend-url>/list/add
  • /listreport<backend-url>/list/retrieve
  • /listremove<backend-url>/list/remove
  • /counter<backend-url>/counter
  • /repstats<backend-url>/rep/get
  • /walkie<backend-url>/walkie

Important: Check Escape Channels, users and links sent to your app for all commands.

Event Subscriptions

  • Request URL:<backend-url>/muzzle/handle
  • Subscribe to Workspace Events:
    • messages.channels
    • reaction_added
    • reaction_removed
    • team_join

OAuth & Permissions

  • Redirect URLs (for search/auth UI):http://localhost:3001 (dev), or your deployed frontend URL
  • Scopes:
    • admin
    • channels:history
    • chat:write:bot
    • chat:write:user
    • commands
    • files:write:user
    • groups:history
    • reactions:read
    • users.profile:read
    • users:read
    • identity.basic (user token scope for OAuth login flow)

Copy your Bot Token and User OAuth Token from the app credentials page.

2. Set Up MySQL Database

# Ensure MySQL is running and create the database
mysql -u <username> -p -e "CREATE DATABASE mockerdbdev;"# Seed the database (if DB_SEED.sql exists in repo root)
mysql -u <username> -p mockerdbdev < DB_SEED.sql

3. Environment Variables

Create .env files in packages/backend and packages/frontend (or set them globally).

For backend, start from the checked-in example:

cp packages/backend/.env.example packages/backend/.env

Backend (packages/backend/.env)

# Slack Bot Credentials
MUZZLE_BOT_TOKEN=xoxb-your-bot-token
MUZZLE_BOT_USER_TOKEN=xoxp-your-user-token
MUZZLE_BOT_SIGNING_SECRET=your-signing-secret
# Slack OAuth (for search/auth UI login)
SLACK_CLIENT_ID=your-client-id
SLACK_CLIENT_SECRET=your-client-secret
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
# Search & Auth
ALLOWED_TEAM_DOMAIN=your-workspace-domain
SEARCH_FRONTEND_URL=http://localhost:3001
SEARCH_AUTH_SECRET=generate-a-random-secret-key
# MySQL / TypeORM
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=your-password
TYPEORM_DATABASE=mockerdbdev
TYPEORM_ENTITIES=/absolute/path/to/mocker/packages/backend/src/shared/db/models/*.ts
TYPEORM_SYNCHRONIZE=true
# API Server
PORT=3000
NODE_ENV=development
# External APIs (optional, for AI features)
OPENAI_API_KEY=sk-your-openai-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key

Frontend (packages/frontend/.env)

For frontend, start from the checked-in example:

cp packages/frontend/.env.example packages/frontend/.env
# Backend API URL
VITE_API_BASE_URL=http://localhost:3000

4. Local Development Setup

# Install dependencies (installs all workspaces)
npm install
# Start backend development server
npm run start
# In a new terminal, start frontend development server
npm run dev -w @mocker/frontend
# Backend: http://localhost:3000# Frontend (search UI): http://localhost:5173

5. Testing

# Run all tests
npm run test# Run only backend tests
npm run test -w @mocker/backend
# Run backend tests in watch mode
npm run test:watch -w @mocker/backend
# Run with coverage
npm run test:coverage -w @mocker/backend

6. Linting & Formatting

# Check linting and format issues
npm run lint
npm run format:check
# Auto-fix linting and formatting issues
npm run lint:fix
npm run format:fix

7. Build for Production

# Build all workspaces
npm run build
# Build only backend
npm run build:backend
# Build backend with production optimizations
npm run build:prod:backend
# Build only frontend
npm run build:frontend

8. Docker

# Build backend Docker image
docker build \
--build-arg PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
-f packages/backend/Dockerfile \
-t mocker-backend:latest \
.# Run Docker container
docker run -p 3000:3000 \
-e TYPEORM_HOST=host.docker.internal \
-e MUZZLE_BOT_TOKEN=xoxb-... \
mocker-backend:latest
# View logs
docker logs <container-id>
docker logs <container-id>| jq .

Key Features

Slack Bot Commands

  • Mock, Define, Muzzle, Counter, Confess, List, Walkie - Various game modes and actions
  • Reputation Tracking - Rep stats, reactions, achievements
  • Event Handling - Real-time reactions, team join events, message history

Search & Auth System (New)

  • OAuth Login - Users authenticate via Slack to access the search UI
  • Team-Scoped Search - Messages are filtered by teamId to prevent cross-workspace leakage
  • Session Tokens - HMAC-signed custom session tokens (base64url payload + signature, not JWT), issued after OAuth callback, required for all search requests
  • Rate Limiting - Auth endpoints: 20/15min, Search endpoints: 60/1min

AI Features (Optional)

  • Sentiment Analysis - Analyzes message tone
  • AI Summaries - Generates summaries of message threads

Schema changes are managed by TypeORM using your configured synchronization settings.

Scheduled Jobs

Most scheduled jobs run inside the backend Node.js process using node-cron. They are started automatically when the server connects to the database.

JobScheduleLocationDescription
Fun Fact0 9 * * * (9 AM ET)In-processPosts daily facts, joke, quote, and on-this-day event to Slack
Pricing10 * * * * (every hour at :10)In-processRecalculates item prices based on median reputation
Health Check*/5 * * * * (every 5 min)Bash scriptChecks the /health endpoint from outside the process and alerts Slack on failure

Fun Fact Job environment variables

VariableDefaultDescription
API_NINJA_KEY(required)API key for api-ninjas.com facts endpoint
FUN_FACT_SLACK_CHANNEL#generalSlack channel to post the daily fun-fact message
FACT_TARGET_COUNT5Number of unique facts to collect per run
MAX_FACT_ATTEMPTS50Maximum fetch attempts before giving up on facts
MAX_JOKE_ATTEMPTS20Maximum fetch attempts before giving up on the joke

Health Check Job (bash script)

The health check job lives in packages/jobs/health-job/script.sh and must be run from outside the Node.js process so it can detect when the server itself is down. Schedule it with an external cron daemon:

# Health check every 5 minutes*/5 **** /path/to/mocker/packages/jobs/health-job/script.sh >> /path/to/logs/health-job.log 2>&1

The script requires bash, curl, grep, mktemp, and tr. It reads environment from the first file found in: JOB_ENV_FILE, script dir/.env, ~/.bash_profile, ~/.profile, or /home/muzzle.lol/.bash_profile.

Available Scripts

From the root directory, you can run:

CommandDescription
npm run startStart the backend development server
npm run start:prodStart the backend in production mode
npm run buildBuild all workspaces
npm run build:backendBuild only the backend
npm run testRun tests across all workspaces
npm run test:backendRun tests for the backend only
npm run lintLint all packages
npm run lint:fixLint and auto-fix issues

To build the backend Docker image locally, first generate the release metadata, then build:

PREVIOUS_RELEASE_SHA="$(git rev-parse HEAD^ 2>/dev/null || true)" \
node packages/backend/scripts/write-release-metadata.js packages/backend/release-metadata.json
docker build -f packages/backend/Dockerfile -t muzzle .

You can also run workspace-specific commands using:

npm run <script> -w @mocker/backend
npm run <script> -w @mocker/frontend

Docker Logs

The backend writes structured JSON logs to stdout so deployed failures can be investigated directly with docker logs.

Each log entry includes:

  • timestamp
  • level
  • module
  • message
  • context
  • error.name
  • error.message
  • error.stack

Useful commands:

docker logs <container-name>
docker logs <container-name>| grep '"level":"error"'
docker logs <container-name>| grep '"module":"AIService"'
docker logs <container-name>| grep '"channelId":"C123"'
docker logs <container-name>| jq .

The context object is where request-specific identifiers live, such as userId, teamId, channelId, itemId, symbol, and prompt text. In production, start with module and message, then use context to isolate the failing request, and finally inspect error.stack for the root cause.

About

Slack App to Automate Mocking Your Friends

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages