Seamlessly connect to Lavalink, NodeLink, and FrequenC nodes for high-quality audio streaming in your Discord bots. Supports Discord.js, Eris, Oceanic.js, and a powerful plugin system.
Explore the docs »
View Demo
·
Report Bug
·
Request Feature
Note
HarmonyLink v2.0+ introduces advanced type safety with Result types, generic filter builders and a better plugin system cause the old one was not good enough ig. See Migration Guide for upgrading from older versions.
Warning
Support for Lavalink Version 3 is planned, but not confirmed to be done.
Table of Contents
- Multi-library support: Discord.js v14, Eris, Oceanic.js
- Plugin system: Extensible with lifecycle management
- Type safety: Full TypeScript support, neverthrow Result types
- Advanced filters: Type-safe audio filter builder
- Robust node/player management
- Modern, well-documented API
URL | Features | Additional Information |
---|---|---|
RhythmCore | Advanced Music bot With AI | Invite |
Example Bot | Verry basic example bot | coming soon |
npm install harmonylink
import { DJSLibrary, HarmonyLink } from "harmonylink";
import { Client, GatewayIntentBits } from "discord.js";
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates]
});
const music = new HarmonyLink({
nodes: [{
name: "Example Node",
host: "localhost",
port: 2333,
password: "youshallnotpass",
secure: false
}],
library: new DJSLibrary(client),
});
client.on("ready", async () => {
// Create a player - using unwrapOr for cleaner code
const player = (await music.createPlayer({
guildId: "YOUR_GUILD_ID",
voiceId: "VOICE_CHANNEL_ID",
textId: "TEXT_CHANNEL_ID"
})).unwrapOr(null);
if (player) {
// Search and play a track
const response = (await music.resolve({
query: "Rick Astley - Never Gonna Give You Up",
requester: client.user
})).unwrapOr(null);
if (response && response.tracks.length > 0) {
player.queue.add(response.tracks[0]);
await player.play();
console.log("Now playing!");
}
}
});
client.login("YOUR_BOT_TOKEN");
Result Types: HarmonyLink uses neverthrow Result types instead of throwing errors. You can handle them in three ways:
// Option 1: Check with .isOk() (explicit)
if (result.isOk()) {
console.log(result.value); // Safe access
} else {
console.error(result.error); // Handle error
}
// Option 2: Use .unwrapOr() (cleaner)
const value = result.unwrapOr(null); // Returns null if error
if (value) {
console.log(value); // Use the value
}
// Option 3: Use .match() (pattern matching)
result.match(
(value) => console.log(value),
(error) => console.error(error)
);
// So it would look something like this:
const tracks = await <Player>.resolve({ query: "Rick Astley - Never Gonna Give You Up"
8000
, requester: client.user })
tracks.match(
(tracks) => <Player>.queue.add(tracks[0]),
(error) => console.error("Failed to resolve track:", error.message)
);
For more examples, please refer to the Documentation
HarmonyLink supports plugins for custom features and integrations. See the Plugin Guide and loadPlugins for details.
Breaking Changes:
-
Result Types: All async methods now return
Result<T, E>
instead of throwing errors// v1.x (old) try { const player = await music.createPlayer(options); } catch (error) { console.error(error); } // v2.x (new) const playerResult = await music.createPlayer(options); if (playerResult.isOk()) { const player = playerResult.value; } else { console.error(playerResult.error); } // Or use unwrapOr for cleaner code const player = (await music.createPlayer(options)).unwrapOr(null);
-
Plugin System: New lifecycle-based plugin architecture
// v1.x (old) class MyPlugin extends Plugin { load() { /* ... */ } } // v2.x (new) class MyPlugin extends AbstractPlugin { async initialize(manager: HarmonyLink): Promise<Result<void, Error>> { // Plugin initialization logic return ok(); } }
-
Import Changes: Package exports have been reorganized
// v1.x (old) import { HarmonyLink } from "harmonylink/dist/HarmonyLink"; // v2.x (new) import { HarmonyLink } from "harmonylink";
New Features in v2.x:
- Type-safe filters: Enhanced audio filter system with better TypeScript support
- Improved error handling: Comprehensive Result types throughout the API
- Better plugin lifecycle: More robust plugin loading and management
For detailed migration assistance, see the full documentation.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the MIT License. See LICENSE
for more information.