{"id":27026841,"url":"https://github.com/devsmsm/discord-patreon","last_synced_at":"2026-02-27T12:05:32.377Z","repository":{"id":285642070,"uuid":"958851287","full_name":"DEVSMSM/discord-patreon","owner":"DEVSMSM","description":"discord-patreon","archived":false,"fork":false,"pushed_at":"2025-04-03T21:56:33.000Z","size":25,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-17T11:34:22.839Z","etag":null,"topics":["discord","discord-bot","discord-js","discord-patreon","patreon","patreon-api","patreon-client"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DEVSMSM.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-01T21:40:33.000Z","updated_at":"2026-01-13T16:02:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"c18e95b0-4d5a-478d-be14-0b4a95ff5239","html_url":"https://github.com/DEVSMSM/discord-patreon","commit_stats":null,"previous_names":["devsmsm/discord-patreon"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/DEVSMSM/discord-patreon","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DEVSMSM%2Fdiscord-patreon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DEVSMSM%2Fdiscord-patreon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DEVSMSM%2Fdiscord-patreon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DEVSMSM%2Fdiscord-patreon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DEVSMSM","download_url":"https://codeload.github.com/DEVSMSM/discord-patreon/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DEVSMSM%2Fdiscord-patreon/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29893702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T09:48:51.284Z","status":"ssl_error","status_checked_at":"2026-02-27T09:48:43.992Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["discord","discord-bot","discord-js","discord-patreon","patreon","patreon-api","patreon-client"],"created_at":"2025-04-04T23:16:00.407Z","updated_at":"2026-02-27T12:05:32.370Z","avatar_url":"https://github.com/DEVSMSM.png","language":"TypeScript","funding_links":["https://www.patreon.com/portal/registration/register-clients"],"categories":[],"sub_categories":[],"readme":"# discord-patreon\n\nA Node.js package for integrating Patreon membership events with Discord, enabling automatic role management based on patron status.\n\n[![npm version](https://img.shields.io/npm/v/discord-patreon.svg)](https://www.npmjs.com/package/discord-patreon)\n[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)\n\n## Features\n\n- ✅ **Track Patreon Memberships**: Monitor new subscriptions, cancellations, and payment status\n- ✅ **Discord Integration**: Automatically retrieve linked Discord user IDs from Patreon\n- ✅ **Status Tracking**: Track active, declined, and cancelled memberships\n- ✅ **Event System**: Event-based architecture for easy integration\n- ✅ **Persistent Cache**: Prevent duplicate events even across application restarts\n- ✅ **Webhook-Compatible**: Can be integrated with Discord webhooks for notifications\n\n## Installation\n\n```bash\nnpm install discord-patreon\n```\n\nOr with Yarn:\n\n```bash\nyarn add discord-patreon\n```\n\n## Quick Start\n\n```javascript\nconst { PatreonEvents } = require('discord-patreon');\n\n// Initialize with your credentials\nconst patreon = new PatreonEvents({\n  accessToken: 'your-patreon-access-token',\n  campaignId: 'your-campaign-id'\n});\n\n// Subscribe to events\npatreon.on('ready', () =\u003e {\n  console.log('Patreon monitoring started!');\n});\n\npatreon.on('subscribed', (member) =\u003e {\n  console.log(`New patron: ${member.fullName} (${member.id})`);\n  console.log(`Discord ID: ${member.discordId || 'Not connected'}`);\n});\n\n// Start monitoring\npatreon.initialize();\n```\n\n## Complete Configuration Options\n\n```javascript\nconst patreon = new PatreonEvents({\n  // Required configuration\n  accessToken: 'your-patreon-access-token',\n  campaignId: 'your-campaign-id',\n  \n  // Optional configuration\n  checkInterval: 60000,         // How often to check for updates (ms), default: 60000 (1 minute)\n  cacheFile: './patreon-cache.json', // Custom cache file path\n  cacheSaveInterval: 300000     // How often to save cache (ms), default: 300000 (5 minutes)\n});\n```\n\n## Available Events\n\n| Event | Description | Parameter |\n|-------|-------------|-----------|\n| `ready` | Emitted when monitoring has started | None |\n| `subscribed` | Emitted when a new patron subscribes | Patron data object |\n| `canceled` | Emitted when a patron cancels their subscription | Patron data object |\n| `declined` | Emitted when a patron's payment is declined | Patron data object |\n| `reactivated` | Emitted when a canceled patron reactivates | Patron data object |\n| `connected` | Emitted when a patron connects their Discord account | Patron data object |\n| `disconnected` | Emitted when a patron disconnects their Discord account | Patron data object |\n| `expired` | Emitted when a membership expires | Patron data object |\n| `error` | Emitted when an error occurs | Error object |\n\n## Patron Data Structure\n\nEach patron object contains:\n\n```typescript\n{\n  id: string;                 // Patreon member ID\n  status: string;             // Status: active_patron, declined_patron, former_patron\n  fullName?: string;          // Patron's full name (if available)\n  email?: string;             // Patron's email (if available)\n  patronStatus?: string;      // Detailed patron status\n  pledgeAmount?: number;      // Amount in dollars (if available)\n  discordId: string | null;   // Discord user ID (if connected)\n  joinedAt?: string;          // When they became a patron\n  expiresAt?: string;         // When their current pledge expires\n  relationships?: any;        // Raw relationships data from Patreon API\n}\n```\n\n## Advanced Usage Examples\n\n### Discord Role Management\n\n```javascript\nconst { Client, GatewayIntentBits } = require('discord.js');\nconst { PatreonEvents } = require('discord-patreon');\n\n// Initialize Discord client\nconst client = new Client({ \n  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers]\n});\n\n// Initialize Patreon events\nconst patreon = new PatreonEvents({\n  accessToken: 'your-patreon-access-token',\n  campaignId: 'your-campaign-id',\n  cacheFile: './patreon-cache.json'\n});\n\n// Set up role management functions\nasync function addPatronRole(discordId) {\n  const guild = client.guilds.cache.get('your-guild-id');\n  if (!guild) return;\n  \n  try {\n    const member = await guild.members.fetch(discordId);\n    if (member) {\n      await member.roles.add('patron-role-id');\n      console.log(`Added patron role to ${member.user.tag}`);\n    }\n  } catch (error) {\n    console.error(`Failed to add role: ${error.message}`);\n  }\n}\n\nasync function removePatronRole(discordId) {\n  const guild = client.guilds.cache.get('your-guild-id');\n  if (!guild) return;\n  \n  try {\n    const member = await guild.members.fetch(discordId);\n    if (member) {\n      await member.roles.remove('patron-role-id');\n      console.log(`Removed patron role from ${member.user.tag}`);\n    }\n  } catch (error) {\n    console.error(`Failed to remove role: ${error.message}`);\n  }\n}\n\n// Handle Patreon events\npatreon.on('ready', () =\u003e {\n  console.log('Patreon monitoring started!');\n});\n\npatreon.on('subscribed', (member) =\u003e {\n  console.log(`New patron: ${member.fullName}`);\n  if (member.discordId) {\n    addPatronRole(member.discordId);\n  }\n});\n\npatreon.on('connected', (member) =\u003e {\n  console.log(`Patron connected Discord: ${member.discordId}`);\n  addPatronRole(member.discordId);\n});\n\npatreon.on('canceled', (member) =\u003e {\n  console.log(`Patron canceled: ${member.fullName}`);\n  if (member.discordId) {\n    removePatronRole(member.discordId);\n  }\n});\n\npatreon.on('declined', (member) =\u003e {\n  console.log(`Patron payment declined: ${member.fullName}`);\n  if (member.discordId) {\n    removePatronRole(member.discordId);\n  }\n});\n\npatreon.on('disconnected', (member) =\u003e {\n  console.log(`Patron disconnected Discord: ${member.discordId}`);\n  removePatronRole(member.discordId);\n});\n\n// Start both systems\nclient.once('ready', () =\u003e {\n  console.log(`Logged in as ${client.user.tag}`);\n  patreon.initialize();\n});\n\nclient.login('your-discord-bot-token');\n```\n\n### Lookup Patrons by Discord ID\n\n```javascript\nconst { PatreonEvents } = require('discord-patreon');\n\nconst patreon = new PatreonEvents({\n  accessToken: 'your-patreon-access-token',\n  campaignId: 'your-campaign-id'\n});\n\n// Initialize and wait for ready event\npatreon.on('ready', () =\u003e {\n  // Now you can look up patrons by Discord ID\n  const checkPatronStatus = (discordId) =\u003e {\n    const patron = patreon.users.get(discordId);\n    \n    if (patron) {\n      console.log(`Found patron: ${patron.fullName}`);\n      console.log(`Status: ${patron.status}`);\n      console.log(`Pledge amount: $${patron.pledgeAmount || 'unknown'}`);\n      return true;\n    } else {\n      console.log(`No patron found with Discord ID: ${discordId}`);\n      return false;\n    }\n  };\n  \n  // Example usage\n  checkPatronStatus('559253955230695426');\n});\n\npatreon.initialize();\n```\n\n## Persistent Cache\n\nThe package includes a robust caching system that:\n\n1. Prevents duplicate events across application restarts\n2. Tracks Discord ID connections and disconnections\n3. Maintains a history of membership status changes\n\nThis ensures your application won't send duplicate welcome messages or assign roles multiple times.\n\n```javascript\n// Configure with a cache file\nconst patreon = new PatreonEvents({\n  accessToken: 'your-patreon-access-token',\n  campaignId: 'your-campaign-id',\n  cacheFile: './data/patreon-cache.json' // Custom location\n});\n\n// The cache will be saved automatically and loaded on restart\n```\n\n## Important Notes\n\n### Patreon API Access\n\nTo use this package, you need:\n\n1. A Patreon Creator account\n2. A Patreon API Client (create one at https://www.patreon.com/portal/registration/register-clients)\n3. An access token with the following scopes:\n   - `identity`\n   - `identity[email]`\n   - `campaigns`\n   - `campaigns.members`\n   - `campaigns.members.address`\n   - `campaigns.members[email]`\n\n### Rate Limits\n\nPatreon has API rate limits. To avoid hitting these limits:\n\n- Use a reasonable `checkInterval` (60000ms or higher recommended)\n\n## Proper Shutdown\n\nTo ensure the cache is saved properly before your application exits:\n\n```javascript\n// Handle graceful shutdown\nprocess.on('SIGINT', () =\u003e {\n  console.log('Shutting down...');\n  patreon.stop(); // This saves the cache and cleans up\n  process.exit(0);\n});\n```\n\n## Troubleshooting\n\n### API Error: 401 Unauthorized\n\n- Your access token may be invalid or expired\n- Ensure you have the required scopes enabled for your token\n\n### Events Not Firing\n\n- Check that you've called `initialize()` after setting up event listeners\n- Verify your campaign ID is correct\n- Ensure your access token has the necessary permissions\n\n### Discord IDs Not Being Retrieved\n\n- Confirm that your patrons have connected their Discord accounts to Patreon\n- Ensure your access token has the `identity` scope\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nISC\n\n## Support\n\nFor questions or support, please contact: `devszero` on Discord.\n\n## Stability Notice\n\n**IMPORTANT**: This package is currently in early development. You may encounter bugs or changes to the API in future versions. Error handling for edge cases is still being refined, and some features might not work as expected with all Patreon account configurations. Please report any issues on the GitHub repository.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevsmsm%2Fdiscord-patreon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevsmsm%2Fdiscord-patreon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevsmsm%2Fdiscord-patreon/lists"}