diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a6217c5b..9b60a9f6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,32 +1,38 @@ -In order to make the review process faster, please take a few minutes to go through this checklist: - -## Acceptance Criteria - -- All of the following criteria must be met: - - [ ] This community is directly tied to an open source project - - [ ] The open source project has an OSI-approved open source license: https://opensource.org/licenses - - [ ] This Discord server is an official channel for the open source project approved by the maintainers of the open source project - - [ ] This Discord server adheres to the [Discord Community Guidelines](https://discord.com/guidelines) -- One of the following criteria must be met: - - [ ] The Discord server has over 1,000 members - - [ ] The open source project has a repository with at least 1,000 GitHub Stars or some equivalent - -## Code Review - -- All of the following criteria must be met: - - [ ] The `"title"` field has a commonly identifiable name of the open source project - - [ ] The `"inviteCode"` field contains a valid invite code (without the full URL), without an expiration date - - [ ] The `"githubUrl"` field is a fully specified and valid URL starting with `https://` and refers to a place where we can easily find your primary open source repositories. GitHub repo or organization URLs (or some equivalent) are acceptable (but not a cloning URL). - - [ ] The `"logo"` field refers to the correct logo file (double-check the extension) - - [ ] The `"quote"` field (if included, it is optional) is about your usage of Discord (and not about the open source project itself) - - [ ] The `"quoteSourceUrl"` field (if included, it is optional) is a fully specified and valid URL starting with `https://` - -## Logo Review - -- All of the following criteria must be met: - - [ ] The logo makes use of transparent elements and does not include its own background - - [ ] The logo will look correct when the non-transparent elements are made solid white - - [ ] The logo is a commonly identifiable logo of the open source project -- One of the following criteria must be met: - - [ ] The logo is an SVG file with 72x72 dimensions and has been optimized with [`svgo`](https://www.npmjs.com/package/svgo) ([Web UI](https://jakearchibald.github.io/svgomg/)) - - [ ] The logo is a PNG file with 144x144 dimensions and has been optimized with [ImageOptim](https://imageoptim.com/) ([Web UI](https://imageoptim.com/online)) +# Review Checklist + +## 1. Acceptance Criteria + +### **All of the following criteria must be met:** +- [ ] This community is directly tied to an open source project. +- [ ] The open source project has an OSI-approved open source license ([List of OSI-approved licenses](https://opensource.org/licenses)). +- [ ] This Discord server is an official channel for the open source project and is approved by the maintainers of the project. +- [ ] This Discord server adheres to the [Discord Community Guidelines](https://discord.com/guidelines). + +### **One of the following criteria must be met:** +- [ ] The Discord server has over **1,000 members**. +- [ ] The open source project has a repository with at least **1,000 GitHub Stars** or an equivalent metric. + +--- + +## 2. Code Review + +### **All of the following criteria must be met:** +- [ ] The `"title"` field contains a commonly identifiable name of the open source project. +- [ ] The `"inviteCode"` field contains a **valid invite code** (not a full URL) without an expiration date. +- [ ] The `"githubUrl"` field is a fully specified and valid URL starting with `https://` and refers to the primary open source repositories (GitHub repo or organization URLs are acceptable, but cloning URLs are not). +- [ ] The `"logo"` field refers to the correct logo file (double-check the file extension). +- [ ] The `"quote"` field (optional) is about your usage of Discord, not about the open source project itself. +- [ ] The `"quoteSourceUrl"` field (optional) is a fully specified and valid URL starting with `https://`. + +--- + +## 3. Logo Review + +### **All of the following criteria must be met:** +- [ ] The logo includes transparent elements and does not include its own background. +- [ ] The logo looks correct when the non-transparent elements are made solid white. +- [ ] The logo is a commonly identifiable logo of the open source project. + +### **One of the following criteria must be met:** +- [ ] The logo is an **SVG file** with **72x72 dimensions** and has been optimized with [`svgo`](https://www.npmjs.com/package/svgo) ([Optimize via Web UI](https://jakearchibald.github.io/svgomg/)). +- [ ] The logo is a **PNG file** with **144x144 dimensions** and has been optimized with [ImageOptim](https://imageoptim.com/) ([Optimize via Web UI](https://imageoptim.com/online)). \ No newline at end of file diff --git a/README.md b/README.md index 73ea7181..16f833d3 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,65 @@ # Open Source Communities on Discord -This is the source list of OSS communities that use and live on Discord. It is used to populate the list at https://discord.com/open-source. +This repository contains the source list of open source software (OSS) communities that utilize Discord. It is used to populate the list displayed on [Discord's Open Source page](https://discord.com/open-source). -Thanks for being part of Discord! +Thank you for being part of Discord! -## Acceptance criteria +--- -At this time, we are accepting communities which meet the following criteria: +## 🎯 Acceptance Criteria -1. Your community is not Discord-focused (for example, Discord bots or modifications are not accepted). -2. Your community has at least 1,000 members, or the GitHub repo has at least 1,000 stars. -3. Your community adheres to the [Discord community guidelines](https://discord.com/guidelines). +To be added to the list, communities must meet the following requirements: -Do you own a large bot? Take a look at [verifying it](https://support.discord.com/hc/en-us/articles/360040720412). +1. **Open Source Focus:** Your community should be tied directly to an open source project and not Discord-focused (e.g., Discord bots or modifications are not accepted). +2. **Community Size:** Your community must have at least **1,000 members**, or the associated GitHub repository must have **1,000 stars** or more. +3. **Guidelines Compliance:** Your community must adhere to the [Discord Community Guidelines](https://discord.com/guidelines). -## Adding your project +> **Note:** If you own a large bot, consider [verifying it](https://support.discord.com/hc/en-us/articles/360040720412) as an alternative. -1. Fork the repo -2. Add your logo into [`/logos`](https://github.com/discord/discord-open-source/tree/master/logos) +--- - * Logo dimensions should be either `72x72` for SVG or `144x144` for PNG. - * Logo should be minified. - * Logo should be monochromatic and white (check [the website](https://discord.com/open-source) for examples) - * SVGs should contain only vector shapes — no raster graphics. +## 🛠 Adding Your Project -3. Add your community to [`communities.json`](https://github.com/discord/discord-open-source/blob/master/communities.json), like so: +Follow these steps to submit your project: + +### 1. Fork the Repository +Fork this repository to your GitHub account. + +### 2. Add Your Logo +Place your project's logo in the [`/logos`](https://github.com/discord/discord-open-source/tree/master/logos) directory. + +- **Dimensions:** + - For SVG: `72x72` + - For PNG: `144x144` +- **Optimization:** The logo should be minified. +- **Style:** The logo must be monochromatic and white (see examples on the [Discord Open Source page](https://discord.com/open-source)). +- **Format:** SVGs should contain only vector shapes (no raster graphics). + +### 3. Update `communities.json` +Add your community details to the [`communities.json`](https://github.com/discord/discord-open-source/blob/master/communities.json) file using the following format: ```json { "title": "Name of your project", - "inviteCode": "The public invite code to your project, usually the code after https://discord.gg/", + "inviteCode": "The public invite code to your project (found after https://discord.gg/).", "githubUrl": "The URL of your GitHub organization or project repository.", "logo": "your-logo.svg", "quote": "Optional: A short quote about how you use Discord for your project.", - "quoteSourceUrl": "Optional: An optional source for the quote." + "quoteSourceUrl": "Optional: A URL providing the source of the quote." } ``` -4. Submit a PR with your change, and if all is well, we'll merge it and display it on Discord's [open source page](https://discord.com/open-source)! +### 4. Submit a Pull Request +- Open a Pull Request (PR) with your changes. +- Ensure all criteria are met and the JSON follows the required structure. +- Once reviewed and approved, your community will be added to Discord's [Open Source page](https://discord.com/open-source). + +--- + +## 📋 Notes + +- Communities tied to Discord-focused projects (e.g., bots, modifications) are not eligible for this list. +- Ensure your logo meets all specifications to avoid delays in approval. +- Join our growing list of open source communities and connect with contributors worldwide! + +💜 Thanks for contributing to open source and for being part of Discord! \ No newline at end of file diff --git a/validate.js b/validate.js index 5950e8f8..31f1e3eb 100644 --- a/validate.js +++ b/validate.js @@ -14,54 +14,71 @@ const { data: communities } = require('./communities.json'); const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); async function validateCommunity(community, error, warn) { - for (const field of ['title', 'inviteCode', 'githubUrl', 'logo']) { + // Required string fields + const requiredFields = ['title', 'inviteCode', 'githubUrl', 'logo']; + for (const field of requiredFields) { if (!community[field] || typeof community[field] !== 'string') { error(`${chalk.bold(field)} field must be present and a string`); } } - const ghUrl = new URL(community.githubUrl); - if (ghUrl.protocol !== 'https:' || ghUrl.pathname.endsWith('.git')) { - error(`${chalk.bold('githubUrl')} should be a valid URL starting with \`https://\` (and not a cloning URL)`); + // Validate GitHub URL + try { + const ghUrl = new URL(community.githubUrl); + if (ghUrl.protocol !== 'https:' || ghUrl.pathname.endsWith('.git')) { + error(`${chalk.bold('githubUrl')} should be a valid URL starting with \`https://\` (and not a cloning URL)`); + } + } catch { + error(`${chalk.bold('githubUrl')} is not a valid URL`); } - if (community.quote) { - if (community.quote.length > 350) { - warn(`${chalk.bold('quote')} field must not have more than 350 characters`); - } + // Validate quote length + if (community.quote && community.quote.length > 350) { + warn(`${chalk.bold('quote')} field must not have more than 350 characters`); } + // Validate quote source URL if (community.quoteSourceUrl) { if (!community.quote) { error(`${chalk.bold('quoteSourceUrl')} field requires the ${chalk.bold('quote')} field`); } - const url = new URL(community.quoteSourceUrl); - if (url.protocol !== 'https:') { - error(`${chalk.bold('quoteSourceUrl')} should be a valid URL starting with \`https://\``); + try { + const url = new URL(community.quoteSourceUrl); + if (url.protocol !== 'https:') { + error(`${chalk.bold('quoteSourceUrl')} should be a valid URL starting with \`https://\``); + } + } catch { + error(`${chalk.bold('quoteSourceUrl')} is not a valid URL`); } } - await fs.stat(`./logos/${community.logo}`); + // Check if logo file exists + try { + await fs.stat(`./logos/${community.logo}`); + } catch { + error(`Logo file ${chalk.bold(community.logo)} not found in /logos folder`); + } + // Validate Discord invite while (true) { - const req = await fetch(`https://discord.com/api/v9/invites/${community.inviteCode}?with_expiration=1`); - const response = await req.json(); + const res = await fetch(`https://discord.com/api/v9/invites/${community.inviteCode}?with_expiration=1`); + const json = await res.json(); - if (response.retry_after) { - console.warn(chalk.yellow(`Rate limited for ${response.retry_after}s, waiting`)); - await delay(response.retry_after * 1000); + if (json.retry_after) { + console.warn(chalk.yellow(`Rate limited for ${json.retry_after}s, waiting...`)); + await delay(json.retry_after * 1000); continue; } - if (!response.guild) { - error(`${community.inviteCode} ${util.inspect(response)}`); + if (!json.guild) { + error(`${community.inviteCode} ${util.inspect(json)}`); } - if (response.expires_at) { - error('Invite must be permanant'); + if (json.expires_at) { + error('Invite must be permanent'); } - if (!response.guild.features.includes('COMMUNITY')) { + if (!json.guild.features.includes('COMMUNITY')) { warn('COMMUNITY feature is not enabled'); } @@ -71,49 +88,47 @@ async function validateCommunity(community, error, warn) { async function validate() { console.log(chalk.underline.bold.white('Validating communities.json')); + const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic); bar.start(communities.length, 0); for (const community of communities) { - const error = (message) => { + const error = (msg) => { process.exitCode = 1; if (process.stderr.clearLine) { process.stderr.clearLine(); process.stderr.cursorTo(0); } - console.error(`${chalk.red.bold(community.title)}: ${message}`); + console.error(`${chalk.red.bold(community.title)}: ${msg}`); }; - const warn = (message) => { + + const warn = (msg) => { if (process.stderr.clearLine) { process.stderr.clearLine(); process.stderr.cursorTo(0); } - console.error(`${chalk.yellow.bold(community.title)}: ${message}`); + console.error(`${chalk.yellow.bold(community.title)}: ${msg}`); }; await validateCommunity(community, error, warn); - bar.increment(); } bar.stop(); - const sorted = communities - .slice(0) - .sort((a, b) => a.title.localeCompare(b.title)); - - for (let i = 0; i < sorted.length; i += 1) { - const a = sorted[i]; - const b = communities[i]; - if (a.title !== b.title) { - console.log(chalk.red(`${chalk.bold(b.title)} is not in alphabetical order!`)); + // Alphabetical order check + const sorted = [...communities].sort((a, b) => a.title.localeCompare(b.title)); + for (let i = 0; i < sorted.length; i++) { + if (sorted[i].title !== communities[i].title) { + console.log(chalk.red(`${chalk.bold(communities[i].title)} is not in alphabetical order!`)); process.exitCode = 1; break; } } } -validate().catch((e) => { +// Entry point +validate().catch((err) => { process.exitCode = 1; - console.error(e); + console.error(err); });