#
Migration from Riffy
This guide helps you migrate from Riffy (JavaScript) to Sonora (Python).
#
Key Differences
#
Installation
# Riffy
npm install riffy
# Sonora
pip install py-sonora
#
Basic Setup
#
Riffy
const { Riffy } = require("riffy");
const { Client } = require("discord.js");
const client = new Client();
const riffy = new Riffy(client, [
{
host: "127.0.0.1",
port: 2333,
password: "youshallnotpass"
}
]);
#
Sonora
from sonora import SonoraClient
import discord
from discord.ext import commands
bot = commands.Bot(command_prefix='!')
sonora = SonoraClient(
lavalink_nodes=[{
"host": "127.0.0.1",
"port": 2333,
"password": "youshallnotpass"
}]
)
#
Event Handling
#
Riffy
riffy.on("nodeConnect", (node) => {
console.log(`Node ${node.name} connected`);
});
riffy.on("trackStart", (player, track) => {
console.log(`Playing ${track.title}`);
});
#
Sonora
from sonora import event_manager, EventType
@event_manager.on(EventType.NODE_READY)
async def on_node_ready(event):
print(f"Node connected: {event.data}")
@event_manager.on(EventType.TRACK_START)
async def on_track_start(event):
track = event.data['track']
print(f"Playing {track.title}")
#
Player Operations
#
Riffy
// Create player
const player = riffy.createPlayer({
guildId: message.guild.id,
voiceChannel: message.member.voice.channel.id,
textChannel: message.channel.id
});
// Play track
const resolve = await riffy.resolve({ query: "your song", source: "youtube" });
await player.play(resolve);
// Control playback
await player.pause();
await player.resume();
await player.stop();
await player.setVolume(50);
#
Sonora
# Get player (auto-created)
player = await sonora.get_player(ctx.guild.id)
# Connect to voice (usually handled by integration)
await player.connect(channel_id, session_id, token)
# Play track
track = await sonora.load_track("ytsearch:your song")
await player.play(track)
# Control playback
await player.pause()
await player.resume()
await player.stop()
await player.set_volume(50)
#
Queue Management
#
Riffy
// Add to queue
await player.queue.add(resolve);
// Skip track
await player.skip();
// Clear queue
player.queue.clear();
#
Sonora
# Add to queue
player.queue.add(track)
# Skip with auto-playback
await player.skip()
# Advanced queue operations
player.queue.shuffle()
player.queue.move(0, 1) # Move track from position 0 to 1
tracks = player.queue.get_view("history", limit=10)
#
Filters and Effects
#
Riffy
// Basic filters
await player.filters.setEqualizer([
{ band: 0, gain: 0.5 },
{ band: 1, gain: 0.3 }
]);
await player.filters.setKaraoke(true);
#
Sonora
# Advanced filters
from sonora import Equalizer, Karaoke
eq = Equalizer()
eq.set_band(0, 0.5)
eq.set_band(1, 0.3)
player.filters.set_filter(eq)
karaoke = Karaoke()
player.filters.set_filter(karaoke)
# Apply and send to Lavalink
await player.set_filters()
# Presets
player.filters.nightcore()
player.filters.bass_boost("high")
#
Search and Loading
#
Riffy
const resolve = await riffy.resolve({
query: "your search",
source: "youtube"
});
#
Sonora
# Direct loading
track = await sonora.load_track("ytsearch:your search")
# Or use plugins
from sonora.plugins import YouTubePlugin
youtube = YouTubePlugin()
results = await youtube.search("your search")
#
Node Management
#
Riffy
const nodes = [
{ host: "node1", port: 2333, password: "pass1" },
{ host: "node2", port: 2333, password: "pass2" }
];
const riffy = new Riffy(client, nodes);
#
Sonora
# Same node configuration
nodes = [
{"host": "node1", "port": 2333, "password": "pass1"},
{"host": "node2", "port": 2333, "password": "pass2"}
]
sonora = SonoraClient(
lavalink_nodes=nodes,
node_pooling=True # Automatic load balancing
)
#
Error Handling
#
Riffy
riffy.on("trackError", (player, track, error) => {
console.error(`Error playing ${track.title}: ${error.message}`);
});
#
Sonora
@event_manager.on(EventType.TRACK_EXCEPTION)
async def on_track_error(event):
player = event.data['player']
track = event.data['track']
error = event.data['error']
print(f"Error playing {track.title}: {error}")
#
Discord Integration
#
Riffy
// Automatic voice handling
client.on("voiceStateUpdate", (oldState, newState) => {
// Handled automatically by Riffy
});
#
Sonora
# Use integration helpers
from sonora.integrations import DiscordPyIntegration
integration = DiscordPyIntegration(bot, sonora)
integration.attach() # Handles voice events automatically
#
Configuration
#
Riffy
const riffy = new Riffy(client, nodes, {
defaultSearchEngine: "youtube",
sendWS: true
});
#
Sonora
sonora = SonoraClient(
lavalink_nodes=nodes,
node_pooling=True,
reconnect_policy={
"max_retries": 5,
"backoff": "exponential"
}
)
#
Common Patterns Migration
#
Command Handler (Riffy → Sonora)
Riffy:
module.exports = {
name: 'play',
execute: async (message, args) => {
const query = args.join(' ');
const resolve = await riffy.resolve({ query, source: "youtube" });
const player = riffy.createPlayer({
guildId: message.guild.id,
voiceChannel: message.member.voice.channel.id,
textChannel: message.channel.id
});
await player.queue.add(resolve);
await message.reply(`Added ${resolve.title} to queue`);
}
};
Sonora:
@bot.command()
async def play(ctx, *, query):
player = await sonora.get_player(ctx.guild.id)
track = await sonora.load_track(f"ytsearch:{query}")
if not track:
return await ctx.send("No results found!")
player.queue.add(track)
await ctx.send(f"🎵 Added **{track.title}** to queue")
# Auto-play if nothing is playing
if not player.queue.current:
await player.skip()
#
Performance Considerations
- Sonora uses asyncio throughout for better performance
- Memory usage is optimized with proper object pooling
- Connection handling includes automatic reconnection
- Queue operations are O(1) for most operations
#
Need Help?
- Check the API Reference for detailed documentation
- Join our Discord server
- View examples in the repository