From 91396768a836b361c4604f8945cd28630fa05822 Mon Sep 17 00:00:00 2001 From: 01zulfi <85733202+01zulfi@users.noreply.github.com> Date: Sat, 10 Jun 2023 16:40:44 +0500 Subject: [PATCH] wip contributing message service --- config.js | 1 + events/ready.js | 7 ++ package-lock.json | 30 ++++++++ package.json | 1 + services/contribute/contribute.service.js | 88 +++++++++++++++++++++++ services/contribute/index.js | 3 + 6 files changed, 130 insertions(+) create mode 100644 services/contribute/contribute.service.js create mode 100644 services/contribute/index.js diff --git a/config.js b/config.js index bf9307f2..59a42fa7 100644 --- a/config.js +++ b/config.js @@ -13,6 +13,7 @@ const config = { gettingHiredChannelId: process.env.DISCORD_GETTING_HIRED_CHANNEL_ID, botSpamPlaygroundChannelId: '513125912070455296', FAQChannelId: '823266307293839401', + contributingOpportunitiesChannelId: '1012271881472192615', }, roles: { NOBOTRoleId: '783764176178774036', diff --git a/events/ready.js b/events/ready.js index 0d607b0f..1918f154 100644 --- a/events/ready.js +++ b/events/ready.js @@ -1,5 +1,7 @@ const { Events } = require('discord.js'); const { guildId } = require('../config'); +const ContributeService = require('../services/contribute'); +const config = require('../config'); module.exports = { name: Events.ClientReady, @@ -10,5 +12,10 @@ module.exports = { // Fetch Guild members on startup to ensure the integrity of the cache const guild = await client.guilds.fetch(guildId); await guild.members.fetch(); + + new ContributeService( + client, + config.channels.contributingOpportunitiesChannelId, + ).schedule(); }, }; diff --git a/package-lock.json b/package-lock.json index 1f758f9d..acafcdb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@discordjs/rest": "^1.7.1", "axios": "^1.4.0", + "cron": "^2.3.1", "discord-api-types": "^0.37.43", "discord.js": "^14.11.0", "dotenv": "^16.1.4", @@ -3671,6 +3672,14 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cron": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.3.1.tgz", + "integrity": "sha512-1eRRlIT0UfIqauwbG9pkg3J6CX9A6My2ytJWqAXoK0T9oJnUZTzGBNPxao0zjodIbPgf8UQWjE62BMb9eVllSQ==", + "dependencies": { + "luxon": "^3.2.1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6513,6 +6522,14 @@ "node": ">=10" } }, + "node_modules/luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -11106,6 +11123,14 @@ "browserslist": "^4.21.5" } }, + "cron": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.3.1.tgz", + "integrity": "sha512-1eRRlIT0UfIqauwbG9pkg3J6CX9A6My2ytJWqAXoK0T9oJnUZTzGBNPxao0zjodIbPgf8UQWjE62BMb9eVllSQ==", + "requires": { + "luxon": "^3.2.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -13163,6 +13188,11 @@ "yallist": "^4.0.0" } }, + "luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", diff --git a/package.json b/package.json index 3b6acd3b..a4fc5262 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@discordjs/rest": "^1.7.1", "axios": "^1.4.0", + "cron": "^2.3.1", "discord-api-types": "^0.37.43", "discord.js": "^14.11.0", "dotenv": "^16.1.4", diff --git a/services/contribute/contribute.service.js b/services/contribute/contribute.service.js new file mode 100644 index 00000000..9a1d7518 --- /dev/null +++ b/services/contribute/contribute.service.js @@ -0,0 +1,88 @@ +const Redis = require('ioredis'); +const { CronJob } = require('cron'); + +class ContributeService { + constructor(client, contributingChannelId) { + this.redis = new Redis(process.env.REDIS_URL); + this.client = client; + this.contributingChannelId = contributingChannelId; + } + + tenDays = 864000000; + + async schedule() { + const job = new CronJob( + '0 12 * * MON', // every Monday at 12:00 PM UTC + async () => { + await this.start(); + }, + null, + true, + 'America/Los_Angeles', + ); + job.start(); + } + + async start() { + const issues = (await ContributeService.fetchGoodFirstIssues()).map((i) => ({ + id: i.id, + html_url: i.html_url, + })); + const filtered = await this.filterIssues(issues); + const issueToSend = ContributeService.issueToSend(issues, filtered); + + if (issueToSend) { + await this.cacheIssue(issueToSend); + await this.sendMessage(`Hey everyone! We have a new **Good First Issue** available for you to work on! Check it out here: ${issueToSend.html_url}`); + } else { + await this.sendMessage('Hey everyone! We don\'t have any new **Good First Issues** available for you to work on right now. Check back later!'); + } + } + + static async fetchGoodFirstIssues() { + const response = await fetch('https://api.github.com/repos/theodinproject/curriculum/issues?labels=Type:%20Good%20First%20Issue'); + const rawIssues = await response.json(); + return rawIssues; + } + + async sendMessage(message) { + const channel = this.client.channels.cache.get(this.contributingChannelId); + await channel.send(message); + } + + static issueToSend(issues, filtered) { + if (issues.length === 0) { + return null; + } + if (filtered.length === 0) { + return issues[0]; + } + return filtered[0]; + } + + async filterIssues(issues) { + const filtered = []; + // eslint-disable-next-line no-restricted-syntax + for (const issue of issues) { + // eslint-disable-next-line no-await-in-loop + const isCached = await this.isIssueCached(issue.id); + if (!isCached) { + filtered.push(issue); + } + } + return filtered; + } + + async cacheIssue(issue) { + await this.redis.set(issue.id, issue.html_url, 'EX', this.tenDays); + } + + async isIssueCached(issueId) { + const issue = await this.redis.get(issueId); + if (issue) return true; + + return false; + } +} + +module.exports = ContributeService; diff --git a/services/contribute/index.js b/services/contribute/index.js new file mode 100644 index 00000000..ea668a2a --- /dev/null +++ b/services/contribute/index.js @@ -0,0 +1,3 @@ +const Contribute = require('./contribute.service'); + +module.exports = Contribute;