patreon-connect-discord
v1.0.0
Published
patreon-connect-discord is a Node.js package that connects Patreon and Discord, allowing you to automatically manage Discord roles based on your users' Patreon membership status. perfect for giving special access to your premium supporters in real-time.
Maintainers
Readme
patreon-connect-discord
patreon-connect-discord is a lightweight Node.js package that seamlessly connects Patreon and Discord, making it easy to automatically manage Discord roles based on your users' Patreon membership status.
Perfect for granting real-time premium access to your supporters!
✨ Features
🔥 Track Patreon Memberships
Monitor new subscriptions, cancellations, and payment statuses automatically.🔗 Discord Integration
Instantly retrieve linked Discord user IDs directly from Patreon.🛡️ Status Tracking
Easily track active, declined, and cancelled memberships in real-time.⚡ Event System
Clean, event-based architecture for effortless integration into your bot or app.💾 Persistent Cache
Smart caching system prevents duplicate events even after application restarts.📣 Webhook-Compatible
Easily integrate with Discord webhooks for instant membership notifications.
Installation
npm install patreon-connect-discordOr with Yarn:
yarn add patreon-connect-discordQuick Start
const { PatreonEvents } = require('patreon-connect-discord');
// Initialize with your credentials
const patreon = new PatreonEvents({
accessToken: 'your-patreon-access-token',
campaignId: 'your-campaign-id'
});
// Subscribe to events
patreon.on('ready', () => {
console.log('Patreon monitoring started!');
});
patreon.on('subscribed', (member) => {
console.log(`New patron: ${member.fullName} (${member.id})`);
console.log(`Discord ID: ${member.discordId || 'Not connected'}`);
});
// Start monitoring
patreon.initialize();Complete Configuration Options
const patreon = new PatreonEvents({
// Required configuration
accessToken: 'your-patreon-access-token',
campaignId: 'your-campaign-id',
// Optional configuration
checkInterval: 60000, // How often to check for updates (ms), default: 60000 (1 minute)
cacheFile: './patreon-cache.json', // Custom cache file path
cacheSaveInterval: 300000 // How often to save cache (ms), default: 300000 (5 minutes)
});Available Events
| Event | Description | Parameter |
|-------|-------------|-----------|
| ready | Emitted when monitoring has started | None |
| subscribed | Emitted when a new patron subscribes | Patron data object |
| canceled | Emitted when a patron cancels their subscription | Patron data object |
| declined | Emitted when a patron's payment is declined | Patron data object |
| reactivated | Emitted when a canceled patron reactivates | Patron data object |
| connected | Emitted when a patron connects their Discord account | Patron data object |
| disconnected | Emitted when a patron disconnects their Discord account | Patron data object |
| expired | Emitted when a membership expires | Patron data object |
| error | Emitted when an error occurs | Error object |
Patron Data Structure
Each patron object contains:
{
id: string; // Patreon member ID
status: string; // Status: active_patron, declined_patron, former_patron
fullName?: string; // Patron's full name (if available)
email?: string; // Patron's email (if available)
patronStatus?: string; // Detailed patron status
pledgeAmount?: number; // Amount in dollars (if available)
discordId: string | null; // Discord user ID (if connected)
joinedAt?: string; // When they became a patron
expiresAt?: string; // When their current pledge expires
relationships?: any; // Raw relationships data from Patreon API
}Advanced Usage Examples
Discord Role Management
const { Client, GatewayIntentBits } = require('discord.js');
const { PatreonEvents } = require('patreon-connect-discord');
// Initialize Discord client
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers]
});
// Initialize Patreon events
const patreon = new PatreonEvents({
accessToken: 'your-patreon-access-token',
campaignId: 'your-campaign-id',
cacheFile: './patreon-cache.json'
});
// Set up role management functions
async function addPatronRole(discordId) {
const guild = client.guilds.cache.get('your-guild-id');
if (!guild) return;
try {
const member = await guild.members.fetch(discordId);
if (member) {
await member.roles.add('patron-role-id');
console.log(`Added patron role to ${member.user.tag}`);
}
} catch (error) {
console.error(`Failed to add role: ${error.message}`);
}
}
async function removePatronRole(discordId) {
const guild = client.guilds.cache.get('your-guild-id');
if (!guild) return;
try {
const member = await guild.members.fetch(discordId);
if (member) {
await member.roles.remove('patron-role-id');
console.log(`Removed patron role from ${member.user.tag}`);
}
} catch (error) {
console.error(`Failed to remove role: ${error.message}`);
}
}
// Handle Patreon events
patreon.on('ready', () => {
console.log('Patreon monitoring started!');
});
patreon.on('subscribed', (member) => {
console.log(`New patron: ${member.fullName}`);
if (member.discordId) {
addPatronRole(member.discordId);
}
});
patreon.on('connected', (member) => {
console.log(`Patron connected Discord: ${member.discordId}`);
addPatronRole(member.discordId);
});
patreon.on('canceled', (member) => {
console.log(`Patron canceled: ${member.fullName}`);
if (member.discordId) {
removePatronRole(member.discordId);
}
});
patreon.on('declined', (member) => {
console.log(`Patron payment declined: ${member.fullName}`);
if (member.discordId) {
removePatronRole(member.discordId);
}
});
patreon.on('disconnected', (member) => {
console.log(`Patron disconnected Discord: ${member.discordId}`);
removePatronRole(member.discordId);
});
// Start both systems
client.once('ready', () => {
console.log(`Logged in as ${client.user.tag}`);
patreon.initialize();
});
client.login('your-discord-bot-token');Lookup Patrons by Discord ID
const { PatreonEvents } = require('patreon-connect-discord');
const patreon = new PatreonEvents({
accessToken: 'your-patreon-access-token',
campaignId: 'your-campaign-id'
});
// Initialize and wait for ready event
patreon.on('ready', () => {
// Now you can look up patrons by Discord ID
const checkPatronStatus = (discordId) => {
const patron = patreon.users.get(discordId);
if (patron) {
console.log(`Found patron: ${patron.fullName}`);
console.log(`Status: ${patron.status}`);
console.log(`Pledge amount: $${patron.pledgeAmount || 'unknown'}`);
return true;
} else {
console.log(`No patron found with Discord ID: ${discordId}`);
return false;
}
};
// Example usage
checkPatronStatus('559253955230695426');
});
patreon.initialize();Persistent Cache
The package includes a robust caching system that:
- Prevents duplicate events across application restarts
- Tracks Discord ID connections and disconnections
- Maintains a history of membership status changes
This ensures your application won't send duplicate welcome messages or assign roles multiple times.
// Configure with a cache file
const patreon = new PatreonEvents({
accessToken: 'your-patreon-access-token',
campaignId: 'your-campaign-id',
cacheFile: './data/patreon-cache.json' // Custom location
});
// The cache will be saved automatically and loaded on restartImportant Notes
Patreon API Access
To use this package, you need:
- A Patreon Creator account
- A Patreon API Client (create one at https://www.patreon.com/portal/registration/register-clients)
- An access token with the following scopes:
identityidentity[email]campaignscampaigns.memberscampaigns.members.addresscampaigns.members[email]
Rate Limits
Patreon has API rate limits. To avoid hitting these limits:
- Use a reasonable
checkInterval(60000ms or higher recommended)
Proper Shutdown
To ensure the cache is saved properly before your application exits:
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('Shutting down...');
patreon.stop(); // This saves the cache and cleans up
process.exit(0);
});Troubleshooting
API Error: 401 Unauthorized
- Your access token may be invalid or expired
- Ensure you have the required scopes enabled for your token
Events Not Firing
- Check that you've called
initialize()after setting up event listeners - Verify your campaign ID is correct
- Ensure your access token has the necessary permissions
Discord IDs Not Being Retrieved
- Confirm that your patrons have connected their Discord accounts to Patreon
- Ensure your access token has the
identityscope
