Another lavalink wrapper but focus on stability and rich features
- Stable client
- Support TypeScript
- 100% Compatible with Lavalink
- Object-oriented
- Easy to setup
- Inbuilt Queue System
- Extendable Player, Queue, Rest class
- Backward compatible (Can run lavalink version 3.7.x)
- Driver based (avaliable to run Nodelink v2 and port older lavalink version)
- Plugin system
- Using PWSL by The PerformanC Organization for better WS implementation
- Compatible with bun
npm i rainlink
Here: https://rainlinkjs.vercel.app/
Name | Creator | Variants |
---|---|---|
ByteBlaze | RainyXeon | Original |
Lunox | adh319 | Original |
If you want to add your own bot create a pull request with your bot added. Please add your full name.
This is the list of all rainlink plugin currently supported
Name | Type | Link | Author |
---|---|---|---|
rainlink-nico | Official | npmjs / github | RainyXeon |
rainlink-deezer | Official | npmjs / github | RainyXeon |
rainlink-apple | Official | npmjs / github | RainyXeon |
rainlink-spotify | Official | npmjs / github | RainyXeon |
rainlink-voice | Official | npmjs / github | RainyXeon |
This is the list of all rainlink driver currently supported (codename is made up by me)
Driver Name | Voice Server | Language | Supported Version | Codename | Notes |
---|---|---|---|---|---|
lavalink/v4/koinu | Lavalink | Java | v4.0.0 - v4.x.x | koinu | |
lavalink/v3/koto | Lavalink | Java | v3.0.0 - v3.7.x | koto | filter and resume in lavalink below v3.4 not supported |
nodelink/v2/nari | Nodelink | Javascript | v2.0.0 - v2.x.x | nari | Some filter mode in nodelink not supported |
frequenc/v1/miku | FrequenC | C | IN TESTING | miku | This driver is in testing so don't use it or you will have errors |
const {Client, GatewayIntentBits} = require('discord.js');
const {Guilds, GuildVoiceStates, GuildMessages, MessageContent} = GatewayIntentBits;
const {Rainlink, Library} = require("rainlink");
const Nodes = [{
name: 'owo',
host: '192.168.0.66',
port: 2333,
auth: 'youshallnotpass',
secure: false,
}];
const client = new Client({intents: [Guilds, GuildVoiceStates, GuildMessages, MessageContent]});
const rainlink = new Rainlink({
library: new Library.DiscordJS(client),
nodes: Nodes,
});
client.on("ready", () => console.log(client.user?.tag + " Ready!"));
rainlink.on('nodeConnect', (node) => console.log(`Lavalink ${node.options.name}: Ready!`));
rainlink.on('nodeError', (node, error) => console.error(`Lavalink ${node.options.name}: Error Caught,`, error));
rainlink.on("nodeClosed", (node) => console.warn(`Lavalink ${node.options.name}: Closed`))
// rainlink.on('debug', (name, info) => console.debug(`Lavalink ${name}: Debug,`, info));
rainlink.on('nodeDisconnect', (node, code, reason) => {
console.warn(`Lavalink ${node.options.name}: Disconnected, Code ${code}, Reason ${reason || 'No reason'}`)
});
rainlink.on("trackStart", (player, track) => {
client.channels.cache.get(player.textId).send({content: `Now playing **${track.title}** by **${track.author}**`})
});
rainlink.on("trackEnd", (player) => {
client.channels.cache.get(player.textId).send({content: `Finished playing`})
});
rainlink.on("queueEmpty", player => {
client.channels.cache.get(player.textId).send({content: `Destroyed player due to inactivity.`})
player.destroy();
});
client.on("messageCreate", async msg => {
if (msg.author.bot) return;
if (msg.content.startsWith("!play")) {
const args = msg.content.split(" ");
const query = args.slice(1).join(" ");
const {channel} = msg.member.voice;
if (!channel) return msg.reply("You need to be in a voice channel to use this command!");
let player = await rainlink.create({
guildId: msg.guild.id,
textId: msg.channel.id,
voiceId: channel.id,
shardId: 0,
volume: 40
})
let result = await rainlink.search(query, {requester: msg.author});
if (!result.tracks.length) return msg.reply("No results found!");
if (result.type === "PLAYLIST") for (let track of result.tracks) player.queue.add(track);
else player.queue.add(result.tracks[0]);
if (!player.playing || !player.paused) player.play();
return msg.reply({content: result.type === "PLAYLIST" ? `Queued ${result.tracks.length} from ${result.playlistName}` : `Queued ${result.tracks[0].title}`});
}
})
client.login('');
src/Interface/Manager.ts
+ RainlinkNodeOptions.legacyWS
+ RainlinkNodeOptions.region
+ src/Player/RainlinkVoice
src/Player/RainlinkPLayer
- RainlinkPlayer.state
- RainlinkPlayer.lastvoiceId
- RainlinkPlayer.region
- RainlinkPlayer.lastRegion
- RainlinkPlayer.serverUpdat
- RainlinkPlayer.sessionId
+ src/Interface/Events.ts
src/index.ts
- Plugin
- Plugin.VoiceReceiver
- Plugin.PlayerMoved
src/Interface/Constants.ts
- VoiceConnect = 'voiceConnect',
- VoiceDisconnect = 'voiceDisconnect',
- VoiceError = 'voiceError',
- VoiceStartSpeaking = 'voiceStartSpeaking',
- VoiceEndSpeaking = 'voiceEndSpeaking',
src/Plugin/RainlinkPlugin.ts
+ isRainlinkPlugin
src/Node/RainlinkRest.ts
- getLavalinkInfo()
+ getInfo()
src/Player/RainlinkPlayer.ts
- <RainlinkPlayer>.setFilter("nightcore")
+ <RainlinkPlayer>.filter.set()
+ <RainlinkPlayer>.filter
src/Interface/Constants.ts
+ RainlinkFilterMode
src/index.ts (Add new class)
+ RainlinkFilter
- @Deivu: Founder of @shipgirlproject and creator of Shoukaku / inspired
- @0t4u: Member of @shipgirlproject / inspired
- @Takiyo0: Creator of Kazagumo / Player and queue inspiration
- @Solaris9: Original creator of erela.js / Event system inspiration
- And everyone who contribute my project π
- @RainyXeon: Owner of Rainlink
- @PAINFUEG0: Tester of #TeamRain