Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
81b0e56
Update bot.js
mannnayla1-code Nov 10, 2026
4e781a2
Update bot.js
mannnayla1-code Nov 10, 2026
9ced70a
Update fish.js
mannnayla1-code Nov 10, 2026
b8d1925
Update fish.js
mannnayla1-code Nov 10, 2026
cf2a9c5
Update items.js
mannnayla1-code Nov 10, 2026
98fa6c4
Update fish.js
mannnayla1-code Nov 10, 2026
503e9cc
feat: Buat Kode Pancingan dan Ramuan Keberuntungan
mannnayla1-code Nov 11, 2026
6c18db0
nerf: kurangi harga ikan & multiplier rod biar lebih susah
mannnayla1-code Nov 11, 2026
4ec18d7
Update fish.js
mannnayla1-code Nov 11, 2026
a964f14
Update items.js
mannnayla1-code Nov 11, 2026
2ff2509
Update items.js
mannnayla1-code Nov 11, 2026
3bb0b18
Update items.js
mannnayla1-code Nov 11, 2026
e9d7080
Update items.js
mannnayla1-code Nov 11, 2026
fc164e2
Update items.js
mannnayla1-code Nov 11, 2026
d1f2629
fix: move premium bonus to fishing
mannnayla1-code Nov 11, 2026
01b85e3
merge: sync latest main for fishing updates
mannnayla1-code Nov 11, 2026
335b5c6
fix: move premium bonus to fishing (#1)
mannnayla1-code Nov 11, 2026
de92c60
Update items.js
mannnayla1-code Nov 11, 2026
0fc9e0c
Update items.js
mannnayla1-code Nov 11, 2026
37a5f72
Update items.js
mannnayla1-code Nov 11, 2026
9fbd79d
Update items.js
mannnayla1-code Nov 11, 2026
3c24ab4
Update items.js
mannnayla1-code Nov 11, 2026
3f1c475
Update fish.js
mannnayla1-code Nov 11, 2026
ef825a6
Update fish.js
mannnayla1-code Nov 11, 2026
a9f1026
feat: update workspace
mannnayla1-code Nov 11, 2026
c42188b
feat: update workspace
mannnayla1-code Nov 11, 2026
a73a1b1
feat: update workspace
mannnayla1-code Nov 11, 2026
3d737b9
feat: update workspace
mannnayla1-code Nov 11, 2026
0d06e5b
merge: sync origin/main into fishing branch
mannnayla1-code Nov 11, 2026
68f1455
fix: smoother luck potion bias; divine & transcendent stay rare
mannnayla1-code Nov 11, 2026
8eb7206
Update fish.js
mannnayla1-code Nov 11, 2026
12d8e19
feat: add Admin Potion (buy 1x, 1min active/cooldown, divine 30%/tran…
mannnayla1-code Nov 11, 2026
6823af3
Merge remote-tracking branch 'origin/main' into trae/main-fishing-update
mannnayla1-code Nov 11, 2026
e67b979
Update items.js
mannnayla1-code Nov 11, 2026
580c0e1
feat: admin potion becomes inventory item; add /use and /give for tra…
mannnayla1-code Nov 11, 2026
e33447c
Merge remote-tracking branch 'origin/main' into trae/main-fishing-update
mannnayla1-code Nov 11, 2026
664a0ef
feat(fish): show AI-generated image of the caught fish
mannnayla1-code Nov 11, 2026
36a8c7a
perf(fish): reply instantly by using image URL directly (no download/…
mannnayla1-code Nov 11, 2026
37fb91f
revert(fish): remove AI image feature (caused delay)
mannnayla1-code Nov 11, 2026
af8a363
feat(buy): remove admin potion purchase cooldown/time gate
mannnayla1-code Nov 11, 2026
4b6e597
feat(admin_potion): remove purchase limit, extend duration to 5 minutes
mannnayla1-code Nov 11, 2026
17b6b40
Update fish.js
mannnayla1-code Nov 11, 2026
dfb06b4
Update fish.js
mannnayla1-code Nov 11, 2026
3f966c6
Update items.js
mannnayla1-code Nov 11, 2026
d1cb05d
Update items.js
mannnayla1-code Nov 11, 2026
7d18483
chore(fish): change admin potion display to gimmick luck text
mannnayla1-code Nov 11, 2026
00ddacc
Merge remote-tracking branch 'origin/main' into trae/main-fishing-update
mannnayla1-code Nov 11, 2026
dea51de
chore(fish): remove gimmick luck percentage from potion message
mannnayla1-code Nov 11, 2026
0719b38
feat(economy): add personal and public fishdex with caught fish images
mannnayla1-code Nov 11, 2026
d621141
feat(fishdex): enable prefix command support
mannnayla1-code Nov 11, 2026
7d90f91
feat(fishdex): show all fish grouped by rarity selector
mannnayla1-code Nov 11, 2026
81c980f
perf(fishdex): shrink fish images to compact thumbnails
mannnayla1-code Nov 11, 2026
afa1d19
Update items.js
mannnayla1-code Nov 11, 2026
92a2efd
feat: Hapus Fitur Musik
mannnayla1-code Nov 11, 2026
4929071
Merge pull request #2 from mannnayla1-code/trae/agent-VJJajp
mannnayla1-code Nov 11, 2026
cea5fbf
Update items.js
mannnayla1-code Nov 11, 2026
20293a1
Update items.js
mannnayla1-code Nov 11, 2026
a6ad442
Update items.js
mannnayla1-code Nov 11, 2026
d24b805
Update items.js
mannnayla1-code Nov 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,5 @@ BACKUP_DIR=./backups
BACKUP_RETENTION_DAYS=14
POSTGRES_RESTORE_URL=

# Music (Lavalink + Riffy) — requires Lavalink v4 nodes
# Default: loads public v4 SSL nodes from lavalink/nodes.json
# Source: https://lavalink.darrennathanael.com/SSL/Lavalink-SSL/
# LAVALINK_NODES_FILE=lavalink/nodes.json
# Optional: override nodes as JSON array (takes priority over nodes file)
# LAVALINK_NODES=[{"host":"lavalink","port":2333,"password":"youshallnotpass","secure":false,"name":"Main"}]
# Self-hosted single node fallback (used only if nodes file/env array are absent):
# LAVALINK_HOST=localhost
# LAVALINK_PORT=2333
# LAVALINK_PASSWORD=youshallnotpass
# LAVALINK_SECURE=false
# LAVALINK_NAME=Main
LAVALINK_SEARCH_PLATFORM=ytmsearch
LAVALINK_REST_VERSION=v4

# Optional Feature/API Keys
TMDB_API_KEY=
23 changes: 1 addition & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,6 @@ TitanBot offers a complete suite of tools for Discord server management and comm
- **Auto Roles** - Assign roles on join
- **Custom Embeds** - Personalized messages

### Music
- **24/7 Mode** - Play music 24/7
- **Interative Button System** - Manage music through buttons
- **Supports EVERY platform** - Supports spotify, deezer, youtube, apple music

</td>
</tr>
</table>
Expand Down Expand Up @@ -125,23 +120,7 @@ TitanBot is fully containerized for easy deployment.
curl http://localhost:3000/health
```

This starts the bot and PostgreSQL. The compose file sets `POSTGRES_SSL=false` and `AUTO_MIGRATE=true` for the bundled database. Music uses public Lavalink v4 nodes from `lavalink/nodes.json` by default.

### Music

Music uses [Lavalink v4](https://github.com/lavalink-devs/Lavalink) via [Riffy](https://github.com/riffy-rb/riffy), similar to [Musicify](https://github.com/codebymitch/Musicify).

1. By default, the bot loads multiple public v4 SSL nodes from [`lavalink/nodes.json`](lavalink/nodes.json) (sourced from [lavalink.darrennathanael.com](https://lavalink.darrennathanael.com/SSL/Lavalink-SSL/)). Edit that file to add or remove nodes.
2. To self-host Lavalink instead, run `docker compose --profile local-lavalink up -d` and set single-node env vars in `.env`:
```env
LAVALINK_HOST=lavalink
LAVALINK_PORT=2333
LAVALINK_PASSWORD=youshallnotpass
LAVALINK_SECURE=false
```
Remove or rename `lavalink/nodes.json` so the bot falls back to those env vars.
3. Override nodes inline with `LAVALINK_NODES` (JSON array) or point at another file with `LAVALINK_NODES_FILE`.
4. Use `/play <song>` from a voice channel, or `/join` to connect without playing. Prefix shortcuts: `join`, `np`, `leave`, `pause`, `resume`, `skip`, `stop`, `volume <0-100>`, or `music <subcommand>`. Use `/nowplaying` and `/queue` for status; `/music` for loop, shuffle, seek, and other controls.
This starts the bot and PostgreSQL. The compose file sets `POSTGRES_SSL=false` and `AUTO_MIGRATE=true` for the bundled database.

### Using GitHub Container Registry

Expand Down
14 changes: 0 additions & 14 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ services:
- POSTGRES_SSL=false
- AUTO_MIGRATE=true
- LOG_LEVEL=${LOG_LEVEL:-warn}
- LAVALINK_NODES_FILE=lavalink/nodes.json
- PORT=3000
- WEB_HOST=0.0.0.0
ports:
Expand All @@ -37,19 +36,6 @@ services:
networks:
- titan-network

lavalink:
profiles:
- local-lavalink
image: ghcr.io/lavalink-devs/lavalink:4
container_name: titanbot-lavalink
restart: unless-stopped
environment:
- LAVALINK_PASSWORD=${LAVALINK_PASSWORD:-youshallnotpass}
volumes:
- ./lavalink/application.yml:/opt/Lavalink/application.yml
networks:
- titan-network

db:
image: postgres:15-alpine
container_name: titanbot-db
Expand Down
27 changes: 0 additions & 27 deletions lavalink/application.yml

This file was deleted.

30 changes: 0 additions & 30 deletions lavalink/nodes.json

This file was deleted.

21 changes: 0 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
"pg": "^8.11.3",
"winston": "^3.19.0",
"winston-daily-rotate-file": "^5.0.0",
"zod": "^3.25.76",
"riffy": "1.0.12"
"zod": "^3.25.76"
},
"engines": {
"node": ">=20.10.0"
Expand Down
12 changes: 0 additions & 12 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import { checkBirthdays } from './services/birthdayService.js';
import { checkGiveaways } from './services/giveawayService.js';
import { loadCommands, registerCommands as registerSlashCommands } from './handlers/loaders/commandLoader.js';
import { runSafeTask, handleTaskError, ErrorCodes } from './utils/errorHandler.js';
import { initializeMusic } from './services/music/riffySetup.js';
import { shutdownMusic } from './services/music/playerHandler.js';
import pkg from '../package.json' with { type: 'json' };
import { EXPECTED_SCHEMA_VERSION, EXPECTED_SCHEMA_LABEL } from './config/database/schemaVersion.js';

Expand Down Expand Up @@ -84,8 +82,6 @@ class TitanBot extends Client {
await this.loadHandlers();
startupLog('Handlers loaded');

initializeMusic(this);

startupLog('Logging into Discord...');
await this.login(this.config.bot.token);
startupLog('Discord login successful');
Expand Down Expand Up @@ -342,10 +338,6 @@ class TitanBot extends Client {
cron.getTasks().forEach(task => task.stop());
logger.info('✅ Cron jobs stopped');

logger.info('Stopping music players...');
await shutdownMusic(this);
logger.info('✅ Music players stopped');

if (this.webServer) {
logger.info('Closing web server...');
await new Promise((resolve) => this.webServer.close(resolve));
Expand Down Expand Up @@ -406,10 +398,6 @@ try {
logger.warn('Recoverable Discord interaction rejection:', reason?.message || reason);
return;
}
if (reason?.message?.includes('Queue is empty')) {
return;
}

// A stray rejection is a bug to fix, not a reason to take the bot down.
// Log loudly with full context; the central task handler categorizes it.
handleTaskError('unhandled_rejection', reason instanceof Error ? reason : new Error(String(reason)), {
Expand Down
1 change: 0 additions & 1 deletion src/commands/Core/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const CATEGORY_ICONS = {
Core: "ℹ️",
Moderation: "🛡️",
Economy: "💰",
Music: "🎵",
Fun: "🎮",
Leveling: "📊",
Utility: "🔧",
Expand Down
24 changes: 23 additions & 1 deletion src/commands/Economy/buy.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import { withErrorHandling, createError, ErrorTypes } from '../../utils/errorHan
import { InteractionHelper } from '../../utils/interactionHelper.js';

const SHOP_ITEMS = shopItems;
const ADMIN_POTION_ID = 'admin_potion';
// Potion ini langsung aktif saat dibeli. Admin potion TIDAK termasuk di sini:
// admin potion disimpan ke inventory dulu supaya bisa dipakai (/use) atau dikirim (/give).
const LUCK_POTION_IDS = new Set([
'luck_potion_1',
'luck_potion_2',
'luck_potion_3',
'luck_potion_4',
'luck_potion_5',
]);
const DEFAULT_LUCK_POTION_DURATION_MS = 5 * 60 * 1000;

export default {
data: new SlashCommandBuilder()
Expand Down Expand Up @@ -136,11 +147,22 @@ export default {
} else if (item.type === "upgrade") {
userData.upgrades[itemId] = true;
successDescription += `\n\n**✨ Your upgrade is now active!**`;
} else if (item.type === "consumable" && LUCK_POTION_IDS.has(itemId)) {
const now = Date.now();
const potionExpiries = userData.luckPotionExpiresAt || {};
const currentExpiry = Math.max(now, Number(potionExpiries[itemId]) || 0);
const duration = Number(item.effect?.duration) || DEFAULT_LUCK_POTION_DURATION_MS;
potionExpiries[itemId] = currentExpiry + (duration * quantity);
userData.luckPotionExpiresAt = potionExpiries;
const activeMinutes = Math.ceil((potionExpiries[itemId] - now) / 60000);
successDescription += `\n\n🧪 **Active for ${activeMinutes} minutes. Timer started at purchase.**`;
} else if (item.type === "consumable" || item.type === "tool") {
userData.inventory[itemId] =
(userData.inventory[itemId] || 0) + quantity;
if (item.type === "tool") {
successDescription += `\n\n**🛠️ ${item.name} added to your inventory!**`;
} else if (itemId === ADMIN_POTION_ID) {
successDescription += `\n\n👑 **Added to your inventory!** Activate it with \`/use item_id:${ADMIN_POTION_ID}\` or gift it with \`/give\`.`;
}
}

Expand All @@ -157,4 +179,4 @@ export default {

await InteractionHelper.safeEditReply(interaction, { embeds: [embed], flags: [MessageFlags.Ephemeral] });
}, { command: 'buy' })
};
};
31 changes: 4 additions & 27 deletions src/commands/Economy/daily.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SlashCommandBuilder } from 'discord.js';
import { createEmbed, errorEmbed, successEmbed, infoEmbed, warningEmbed } from '../../utils/embeds.js';
import { getEconomyData, setEconomyData } from '../../utils/economy.js';
import { getGuildConfig } from '../../services/config/guildConfig.js';
import { formatDuration } from '../../utils/embeds.js';
import { withErrorHandling, createError, ErrorTypes } from '../../utils/errorHandler.js';
import { logger } from '../../utils/logger.js';
Expand All @@ -10,7 +9,6 @@ import { botConfig } from '../../config/bot.js';

const DAILY_COOLDOWN = 24 * 60 * 60 * 1000;
const DAILY_AMOUNT = botConfig.economy?.dailyAmount ?? 100;
const PREMIUM_BONUS_PERCENTAGE = 0.1;

export default {
data: new SlashCommandBuilder()
Expand Down Expand Up @@ -50,25 +48,7 @@ export default {
);
}

const guildConfig = await getGuildConfig(client, guildId);
const PREMIUM_ROLE_ID = guildConfig.premiumRoleId;

let earned = DAILY_AMOUNT;
let bonusMessage = "";
let hasPremiumRole = false;

if (
PREMIUM_ROLE_ID &&
interaction.member &&
interaction.member.roles.cache.has(PREMIUM_ROLE_ID)
) {
const bonusAmount = Math.floor(
DAILY_AMOUNT * PREMIUM_BONUS_PERCENTAGE,
);
earned += bonusAmount;
bonusMessage = `\n✨ **Premium Bonus:** +$${bonusAmount.toLocaleString()}`;
hasPremiumRole = true;
}
const earned = DAILY_AMOUNT;

userData.wallet = (userData.wallet || 0) + earned;
userData.lastDaily = now;
Expand All @@ -80,25 +60,22 @@ export default {
guildId,
amount: earned,
newWallet: userData.wallet,
hasPremium: hasPremiumRole,
timestamp: new Date().toISOString()
});

const embed = successEmbed(
"✅ Daily Claimed!",
`You have claimed your daily **$${earned.toLocaleString()}**!${bonusMessage}`
`You have claimed your daily **$${earned.toLocaleString()}**!`
)
.addFields({
name: "New Cash Balance",
value: `$${userData.wallet.toLocaleString()}`,
inline: true,
})
.setFooter({
text: hasPremiumRole
? `Next claim in 24 hours. (Premium Active)`
: `Next claim in 24 hours.`,
text: `Next claim in 24 hours.`,
});

await InteractionHelper.safeEditReply(interaction, { embeds: [embed] });
}, { command: 'daily' })
};
};
Loading