From 9661ba92d599dffda55356206a982548712ac462 Mon Sep 17 00:00:00 2001 From: Chris Ham <431647+greenham@users.noreply.github.com> Date: Sat, 16 Aug 2025 12:06:40 -0700 Subject: [PATCH] Add Docker Compose setup and clean up legacy code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add docker-compose.yml with volume mounts for config/sfx - Simplify npm scripts (up/down/build/restart/logs) - Update README.md and CLAUDE.md with new commands - Remove unused lib/ directory (migrated to src/) - Update package.json scripts to cleaner naming Benefits: - Update configs and sound effects without rebuilding image - Simplified Docker workflow management - Cleaner project structure 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 28 ++- README.md | 51 +++-- conf/ghbot.abcomg | 438 +++++++++++++++++++++++++++++++++++++++- docker-compose.yml | 62 ++++++ lib/ankhbot-commands.js | 49 ----- lib/static-commands.js | 53 ----- lib/utils.js | 50 ----- package.json | 9 +- 8 files changed, 557 insertions(+), 183 deletions(-) create mode 100644 docker-compose.yml delete mode 100755 lib/ankhbot-commands.js delete mode 100755 lib/static-commands.js delete mode 100755 lib/utils.js diff --git a/CLAUDE.md b/CLAUDE.md index 5e27ebc..391ee68 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,22 +18,30 @@ pnpm install pnpm start # Production mode pnpm dev # Development mode with auto-reload -# Docker commands -pnpm docker:build # Build Docker image -pnpm docker:run # Run Docker container with auto-restart +# Docker Compose (recommended) +pnpm up # Start bot with Docker Compose +pnpm down # Stop bot +pnpm restart # Restart bot (useful after config changes) +pnpm logs # View logs +pnpm build # Rebuild image + +# Direct Docker (alternative) +pnpm image:build # Build Docker image +pnpm image:run # Run Docker container with auto-restart ``` ### Docker Management ```bash -# Stop and remove container -docker stop ghbot && docker rm ghbot +# Docker Compose approach (recommended) +pnpm up && pnpm logs # Start and follow logs +pnpm restart # Restart after config changes +pnpm build && pnpm up # Rebuild after code changes -# View logs -docker logs ghbot -f - -# Rebuild and restart -docker stop ghbot && docker rm ghbot && pnpm docker:build && pnpm docker:run +# Direct Docker approach +docker stop discord-bot && docker rm discord-bot +docker logs discord-bot -f +pnpm image:build && pnpm image:run ``` ## Architecture & Key Components diff --git a/README.md b/README.md index bf07d81..5f66424 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ A modern Discord bot built with Discord.js v14 that provides sound effects, text ### 💬 Text Commands - **Fun Facts**: Random or specific fact retrieval (`!funfact [number]`) -- **Ham Facts**: Ham radio related facts (`!hamfact [number]`) +- **Ham Facts**: Ham-related facts (`!hamfact [number]`) - **Static Commands**: Custom text responses loaded from configuration files - **Ankhbot Import**: Support for imported Ankhbot command databases @@ -80,28 +80,39 @@ A modern Discord bot built with Discord.js v14 that provides sound effects, text ## 🐳 Docker Deployment -### Build and Run +### Recommended: Docker Compose + +```bash +# Start the bot with Docker Compose +pnpm up + +# View logs +pnpm logs + +# Restart the bot (useful after config changes) +pnpm restart + +# Stop the bot +pnpm down + +# Rebuild and restart (after code changes) +pnpm build && pnpm up +``` + +**Benefits of Docker Compose:** +- Update `config.json`, `sfx/`, and `conf/` files without rebuilding the image +- Automatic restart on failure +- Easy log management +- Resource limits and health checks + +### Alternative: Direct Docker ```bash # Build the Docker image -pnpm docker:build +pnpm image:build # Run the container -pnpm docker:run -``` - -### Using Docker Compose - -```yaml -version: "3.8" -services: - ghbot: - build: . - restart: unless-stopped - volumes: - - ./config.json:/app/config.json:ro - - ./sfx:/app/sfx:ro - - ./conf:/app/conf:ro +pnpm image:run ``` ## 📖 Usage @@ -149,21 +160,25 @@ services: ### Creating a Discord Bot 1. **Go to Discord Developer Portal** + - Visit https://discord.com/developers/applications - Click "New Application" and give it a name 2. **Create Bot User** + - Go to the "Bot" section - Click "Add Bot" - Copy the bot token for your config.json 3. **Enable Required Intents** Under "Privileged Gateway Intents", enable: + - **SERVER MEMBERS INTENT** (Required for role management) - **MESSAGE CONTENT INTENT** (Required for prefix commands) 4. **Bot Permissions** When inviting the bot, ensure it has these permissions: + - Send Messages - Embed Links - Read Message History diff --git a/conf/ghbot.abcomg b/conf/ghbot.abcomg index 78f177b..3fdb86d 100755 --- a/conf/ghbot.abcomg +++ b/conf/ghbot.abcomg @@ -1 +1,437 @@ -[{"Command":"!flavor","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"All braise be to our Lord and Savior, Flavor!","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!stopit","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://youtu.be/Ow0lr63y4Mw?t=6","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!nevergiveup","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.youtube.com/watch?v=KxGRhd_iWuE","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!rip","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://youtu.be/F-glHAzXi_M","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!ff4battle","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.youtube.com/watch?v=__-NKgcRM2c","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!castlegiant","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://youtu.be/VV1kd7Fx8Uk?t=1431","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!dash","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"DASH!!! https://www.twitch.tv/videos/46709456","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!imdabes","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"IM DA BES: https://www.youtube.com/watch?v=ZVUyyHYkBHk","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!dancebetch","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://clips.twitch.tv/EsteemedIncredulousBaguetteTBTacoRight","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!rollindeep","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"dear gods, that \"rollin' deep\" sounds like someone trying to be hip and failing worse than a dad joke mixed with an adult trying to be hip and \"with it\" to their high school aged children.","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!discord","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"The Curing Chamber Discord: https://discord.gg/tDtyBss","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!twitter","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://twitter.com/greenhamSRL","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!youtube","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.youtube.com/channel/UC83WD1NtGDC54c7210BsuBQ","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!undressme","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://clips.twitch.tv/LitigiousCooperativeKeyboardBloodTrail","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!donger","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"ヽ༼ຈل͜ຈ༽ノ","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!cleveland","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.youtube.com/watch?v=ysmLA5TqbIY","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!nrg","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"༼ つ ◕_◕ ༽つ STRIMMER TAKE MY ENERGY ༼ つ ◕_◕ ༽つ","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!late","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.youtube.com/watch?v=wBqM2ytqHY4","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!store","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.youtube.com/watch?v=iRZ2Sh5-XuM","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"store","FKSoundFile":null},{"Command":"!slut","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"@i_smoke_meth_daily69 HeyGuys","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"SC","Enabled":false,"SoundFile":"","FKSoundFile":null},{"Command":"!antipb","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.twitch.tv/videos/49126397","Cooldown":1,"UserCooldown":1,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!rboroutes","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"Testing New Route w/New PW: https://docs.google.com/spreadsheets/d/1sPr5b_1vlrg65txCtiFiyDSmtbjBsjotJ5cbtJ3iQP0/edit?usp=sharing","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"SC","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!100mgroute","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://docs.google.com/spreadsheets/d/1SD3Nzjc5tIes3Kw5uFnzke89gDxJeq02tvOSKfykd10/edit?usp=sharing","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!celeryman","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://www.youtube.com/watch?v=maAFcEU6atk","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!muttwalksin","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://clips.twitch.tv/PhilanthropicClearCourgetteSoonerLater","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"SC","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!jesus","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://clips.twitch.tv/FaithfulAstuteOcelot4Head","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!muttgasm","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://imgur.com/a/8XWiw5f","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"A","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!invert","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"This is a race of a romhack of ALttP that inverts the controller inputs upon taking damage. UP becomes DOWN! LEFT becomes RIGHT! Y becomes A! B becomes X! MADNESS ENSUES! ThisIsFun","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"SC","Enabled":true,"SoundFile":"","FKSoundFile":null},{"Command":"!freespeech","Permission":"Everyone","Info":"","Group":"DEFAULT","Response":"https://xkcd.com/1357/","Cooldown":0,"UserCooldown":0,"Cost":0,"Count":0,"Usage":"SC","Enabled":true,"SoundFile":"","FKSoundFile":null}] \ No newline at end of file +[ + { + "Command": "!flavor", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "All braise be to our Lord and Savior, Flavor!", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!stopit", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://youtu.be/Ow0lr63y4Mw?t=6", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!nevergiveup", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.youtube.com/watch?v=KxGRhd_iWuE", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!rip", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://youtu.be/F-glHAzXi_M", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!ff4battle", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.youtube.com/watch?v=__-NKgcRM2c", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!castlegiant", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://youtu.be/VV1kd7Fx8Uk?t=1431", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!dash", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "DASH!!! https://www.twitch.tv/videos/46709456", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!imdabes", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "IM DA BES: https://www.youtube.com/watch?v=ZVUyyHYkBHk", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!dancebetch", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://clips.twitch.tv/EsteemedIncredulousBaguetteTBTacoRight", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!rollindeep", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "dear gods, that \"rollin' deep\" sounds like someone trying to be hip and failing worse than a dad joke mixed with an adult trying to be hip and \"with it\" to their high school aged children.", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!discord", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "The Curing Chamber Discord: https://discord.gg/tDtyBss", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!twitter", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://twitter.com/greenhamSRL", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!youtube", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.youtube.com/channel/UC83WD1NtGDC54c7210BsuBQ", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!undressme", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://clips.twitch.tv/LitigiousCooperativeKeyboardBloodTrail", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!donger", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "ヽ༼ຈل͜ຈ༽ノ", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!cleveland", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.youtube.com/watch?v=ysmLA5TqbIY", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!nrg", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "༼ つ ◕_◕ ༽つ STRIMMER TAKE MY ENERGY ༼ つ ◕_◕ ༽つ", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!late", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.youtube.com/watch?v=wBqM2ytqHY4", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!store", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.youtube.com/watch?v=iRZ2Sh5-XuM", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "store", + "FKSoundFile": null + }, + { + "Command": "!slut", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "@i_smoke_meth_daily69 HeyGuys", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "SC", + "Enabled": false, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!antipb", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.twitch.tv/videos/49126397", + "Cooldown": 1, + "UserCooldown": 1, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!rboroutes", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "Testing New Route w/New PW: https://docs.google.com/spreadsheets/d/1sPr5b_1vlrg65txCtiFiyDSmtbjBsjotJ5cbtJ3iQP0/edit?usp=sharing", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "SC", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!100mgroute", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://docs.google.com/spreadsheets/d/1SD3Nzjc5tIes3Kw5uFnzke89gDxJeq02tvOSKfykd10/edit?usp=sharing", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!celeryman", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://www.youtube.com/watch?v=maAFcEU6atk", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!muttwalksin", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://clips.twitch.tv/PhilanthropicClearCourgetteSoonerLater", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "SC", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!jesus", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://clips.twitch.tv/FaithfulAstuteOcelot4Head", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!muttgasm", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://imgur.com/a/8XWiw5f", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "A", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!invert", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "This is a race of a romhack of ALttP that inverts the controller inputs upon taking damage. UP becomes DOWN! LEFT becomes RIGHT! Y becomes A! B becomes X! MADNESS ENSUES! ThisIsFun", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "SC", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + }, + { + "Command": "!freespeech", + "Permission": "Everyone", + "Info": "", + "Group": "DEFAULT", + "Response": "https://xkcd.com/1357/", + "Cooldown": 0, + "UserCooldown": 0, + "Cost": 0, + "Count": 0, + "Usage": "SC", + "Enabled": true, + "SoundFile": "", + "FKSoundFile": null + } +] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d515134 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,62 @@ +version: '3.8' + +services: + discord-bot: + build: + context: . + dockerfile: Dockerfile + container_name: discord-bot + restart: unless-stopped + volumes: + # Configuration files (read-only) + - ./config.json:/app/config.json:ro + - ./conf:/app/conf:ro + + # Sound effects directory (read-only) + - ./sfx:/app/sfx:ro + + # Optional: Mount logs directory if you want persistent logs + # - ./logs:/app/logs + + # Optional: Environment variables + environment: + - NODE_ENV=production + # - DEBUG=true # Uncomment for debug mode + + # Optional: Resource limits + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 256M + + # Optional: Health check + healthcheck: + test: ["CMD", "node", "-e", "process.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Optional: Add a volume backup service + # backup: + # image: alpine:latest + # container_name: ghbot-backup + # volumes: + # - ./config.json:/backup/config.json:ro + # - ./conf:/backup/conf:ro + # - ./sfx:/backup/sfx:ro + # command: | + # sh -c " + # echo 'Creating backup...' + # tar czf /backup/ghbot-backup-$(date +%Y%m%d_%H%M%S).tar.gz -C /backup config.json conf sfx + # echo 'Backup complete' + # " + # profiles: + # - backup + +# Optional: Create named volumes for persistent data +# volumes: +# ghbot_logs: +# driver: local \ No newline at end of file diff --git a/lib/ankhbot-commands.js b/lib/ankhbot-commands.js deleted file mode 100755 index bb804ea..0000000 --- a/lib/ankhbot-commands.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - exists: exists, - get: get -}; - -const fs = require('fs'), - path = require('path'); - -const cmdPrefix = '!'; -const commandsFilePath = path.join(__dirname, '..', 'conf', 'ghbot.abcomg'); -let commands = parseCommands(commandsFilePath); -fs.watchFile(commandsFilePath, (curr, prev) => { - if (curr.mtime !== prev.mtime) { - commands = require(commandsFilePath); - } -}); - -function exists(command) -{ - let matches = commands.filter(obj => { return obj.Command === cmdPrefix+command; }); - if (matches.length > 0) { - return true; - } else { - return false; - } -} - -function get(command) -{ - let matches = commands.filter(obj => { return obj.Command === cmdPrefix+command; }); - if (matches.length > 0) { - let match = matches[0]; - if (match.Enabled === true) { - return matches[0].Response; - } else { - return undefined; - } - } else { - return undefined; - } -} - -// Read in the array of objects exported from AnkhBot -function parseCommands(filePath) -{ - let data = fs.readFileSync(filePath, 'utf-8'); - let commands = eval(data); - return commands; -} \ No newline at end of file diff --git a/lib/static-commands.js b/lib/static-commands.js deleted file mode 100755 index 0c36813..0000000 --- a/lib/static-commands.js +++ /dev/null @@ -1,53 +0,0 @@ -module.exports = { - exists: exists, - get: get -}; - -const fs = require('fs'), - path = require('path'); - -const commandsFilePath = path.join(__dirname, '..', 'conf', 'text_commands'); - -// Read in basic text commands / definitions and watch for changes -let commands = parseCommands(commandsFilePath); -fs.watchFile(commandsFilePath, (curr, prev) => { - if (curr.mtime !== prev.mtime) { - commands = parseCommands(commandsFilePath); - } -}); - -// Read/parse text commands from the "database" -function parseCommands(filePath) -{ - let commands = {}; - let data = fs.readFileSync(filePath, 'utf-8'); - let commandLines = data.toString().split('\n'); - let commandParts; - let aliases; - commandLines.forEach(function(line) { - if (line.length > 0 && line.indexOf('|') !== -1) { - commandParts = line.split('|'); - // check for aliases - aliases = commandParts[0].split(','); - aliases.forEach(function(cmd) { - commands[cmd] = commandParts[1]; - //console.log(`!${cmd},`); - }); - } - }); - return commands; -} - -function exists(command) -{ - return commands.hasOwnProperty(command); -} - -function get(command) -{ - if (exists(command)) { - return commands[command]; - } else { - return undefined; - } -} \ No newline at end of file diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100755 index 13e4b69..0000000 --- a/lib/utils.js +++ /dev/null @@ -1,50 +0,0 @@ -// Converts seconds to human-readable time -String.prototype.toHHMMSS = function () { - let sec_num = parseInt(this, 10); // don't forget the second param - let hours = Math.floor(sec_num / 3600); - let minutes = Math.floor((sec_num - hours * 3600) / 60); - let seconds = sec_num - hours * 3600 - minutes * 60; - - if (hours < 10) { - hours = "0" + hours; - } - if (minutes < 10) { - minutes = "0" + minutes; - } - if (seconds < 10) { - seconds = "0" + seconds; - } - return hours + ":" + minutes + ":" + seconds; -}; - -async function asyncForEach(array, callback) { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array); - } -} - -function randElement(arr) { - return arr[Math.floor(Math.random() * arr.length)]; -} - -function randSort() { - return 0.5 - Math.random(); -} - -function chunkSubstr(str, size) { - const numChunks = Math.ceil(str.length / size); - const chunks = new Array(numChunks); - - for (let i = 0, o = 0; i < numChunks; ++i, o += size) { - chunks[i] = str.substr(o, size); - } - - return chunks; -} - -module.exports = { - asyncForEach, - randElement, - randSort, - chunkSubstr -}; diff --git a/package.json b/package.json index 52b4cd7..843058d 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,13 @@ "scripts": { "start": "node src/index.js", "dev": "nodemon src/index.js", - "docker:build": "docker build -t ghbot:${VERSION:-latest} .", - "docker:run": "docker run -d --name ghbot --restart always ghbot:${VERSION:-latest}" + "up": "docker compose up -d", + "down": "docker compose down", + "build": "docker compose build", + "restart": "docker compose restart", + "logs": "docker compose logs -f", + "image:build": "docker build -t ghbot:${VERSION:-latest} .", + "image:run": "docker run -d --name ghbot --restart always ghbot:${VERSION:-latest}" }, "author": "https://github.com/greenham", "license": "MIT"