Within the final article, I mentioned making a GitHub stars monitor.
On this article, I wish to present you how one can learn about new stars day by day.
We’ll be taught:
- The way to construct a generic system to create and use suppliers.
- The way to use the suppliers to ship notifications.
- Totally different use instances of utilizing completely different suppliers.
Your background job platform 🔌
Trigger.dev is an open-source library that allows you to create and monitor long-running jobs on your app with NextJS, Remix, Astro, and so many extra!
Please assist us with a star 🥹.
It might assist us to create extra articles like this 💖
Star the Trigger.dev repository ⭐️
Let’s set it up 🔥
We’re going to create completely different suppliers to tell us when there are new stars. We’ll arrange Electronic mail
, SMS
, Slack
, and Discord
notifications.
Our aim is to make it easy sufficient for each contributor to contribute extra suppliers sooner or later.
Every supplier can have a special set of parameters, some solely API keys
and a few telephone numbers, relying on the suppliers.
To validate these keys, let’s set up zod
; it’s an awesome library to outline schemas and test knowledge in opposition to them.
You can begin by operating:
npm set up zod --save
As soon as that’s finished, create a brand new folder referred to as suppliers
after which a brand new file inside referred to as register.supplier.ts
.
right here is the code of the file:
import {Schema} from "zod";
export operate registerProvider<T>(
identify: string,
choices: {energetic: boolean},
validation: Schema<T>,
run: (libName: string, stars: quantity, values: T) => Promise<void>
) {
// if not energetic, we are able to simply move an empty operate, nothing will run
if (!choices.energetic) {
return () => {};
}
// will validate and take away pointless values (Safety clever)
const env = validation.parse(course of.env);
// return the operate we are going to run on the finish of the job
return async (libName: string, stars: quantity) => {
console.log(`Operating supplier ${identify}`);
await run(libName, stars, env as T);
console.log(`Completed operating supplier ${identify}`);
}
}
It’s not a number of code, nevertheless it is perhaps somewhat bit complicated.
We’re beginning by creating a brand new operate referred to as registerProvider
. That operate will get a generic kind T
, which is principally our required surroundings variables.
Then we have now 4 extra parameters:
- identify – that may be any of
Twilio
,Discord
,Slack
, orResend
. - choices – at present, one parameter is the supplier energetic or not?
- validation – right here, we move the
zod
schema of our required parameters in our .env file. - run – That really features to ship the notifications. Concentrate that the parameters which can be being handed into it are the library identify, the variety of stars, and the surroundings variables that we laid out in
validation
Then we have now the precise operate:
First, we test if the supplier is energetic or not. If not, we ship an empty operate.
Then, we validate and extract the variables we specify in our schema. If the variables are lacking zod
will ship an error and won’t let the applying run.
Lastly, we return a operate that will get the library identify and the variety of stars and triggers the notification.
Inside our suppliers
folder, create a brand new file referred to as suppliers.ts
and add the next code inside:
export const Suppliers = [];
Later, we are going to add all our suppliers there.
Modify TriggerDev jobs
This text is a continuation of the earlier article on making a GitHub stars monitor.
Edit the file jobs/sync.stars.ts
and add the next code to the underside of the file:
const triggerNotification = consumer.defineJob({
id: "trigger-notification",
identify: "Set off Notification",
model: "0.0.1",
set off: invokeTrigger({
schema: z.object({
stars: z.quantity(),
library: z.string(),
providerNumber: z.quantity(),
})
}),
run: async (payload, io, ctx) => {
await io.runTask("trigger-notification", async () => {
return Suppliers[payload.providerNumber](payload.library, payload.stars);
});
}
});
This job will get the variety of stars, library identify, and supplier quantity and triggers the notification for a particular supplier from the beforehand outlined suppliers.
Now, let’s go forward and modify getStars
on the finish of the operate add the next code:
for (let i = 0; i < Suppliers.size; i++) {
await triggerNotification.invoke(payload.identify + '-' + i, {
library: payload.identify,
stars: stargazers_count - payload.previousStarCount,
providerNumber: i,
});
}
It will set off a notification for each library.
The complete web page code:
import { cronTrigger, invokeTrigger } from "@set off.dev/sdk";
import { consumer } from "@/set off";
import { prisma } from "../../helper/prisma";
import axios from "axios";
import { z } from "zod";
import {Suppliers} from "@/suppliers/suppliers";
// Your first job
// This Job can be triggered by an occasion, log a joke to the console, after which wait 5 seconds earlier than logging the punchline.
consumer.defineJob({
id: "sync-stars",
identify: "Sync Stars Every day",
model: "0.0.1",
// Run a cron daily at 23:00 AM
set off: cronTrigger({
cron: "0 23 * * *",
}),
run: async (payload, io, ctx) => {
const repos = await io.runTask("get-stars", async () => {
// get all libraries and present quantity of stars
return await prisma.repository.groupBy({
by: ["name"],
_sum: {
stars: true,
},
});
});
//loop via all repos and invoke the Job that will get the newest stars
for (const repo of repos) {
await getStars.invoke(repo.identify, );
}
},
});
const getStars = consumer.defineJob({
id: "get-latest-stars",
identify: "Get newest stars",
model: "0.0.1",
// Run a cron daily at 23:00 AM
set off: invokeTrigger({
schema: z.object({
identify: z.string(),
previousStarCount: z.quantity(),
}),
}),
run: async (payload, io, ctx) => {
const stargazers_count = await io.runTask("get-stars", async () => {
const {knowledge} = await axios.get(`https://api.github.com/repos/${payload.identify}`, {
headers: {
authorization: `token ${course of.env.TOKEN}`,
},
});
return knowledge.stargazers_count as quantity;
});
await io.runTask("upsert-stars", async () => {
await prisma.repository.upsert({
the place: {
name_day_month_year: {
identify: payload.identify, month: new Date().getMonth() + 1, yr: new Date().getFullYear(), day: new Date().getDate(),
},
}, replace: {
stars: stargazers_count - payload.previousStarCount,
}, create: {
identify: payload.identify, stars: stargazers_count - payload.previousStarCount, month: new Date().getMonth() + 1, yr: new Date().getFullYear(), day: new Date().getDate(),
},
});
});
for (let i = 0; i < Suppliers.size; i++) {
await triggerNotification.invoke(payload.identify + '-' + i, {
library: payload.identify,
stars: stargazers_count - payload.previousStarCount,
providerNumber: i,
});
}
},
});
const triggerNotification = consumer.defineJob({
id: "trigger-notification",
identify: "Set off Notification",
model: "0.0.1",
set off: invokeTrigger({
schema: z.object({
stars: z.quantity(),
library: z.string(),
providerNumber: z.quantity(),
})
}),
run: async (payload, io, ctx) => {
await io.runTask("trigger-notification", async () => {
return Suppliers[payload.providerNumber](payload.library, payload.stars);
});
}
});
Now, the enjoyable half 🎉
Let’s go forward and create our suppliers!
First create a brand new folder referred to as suppliers/lists
1. Discord
Create a brand new file referred to as discord.supplier.ts
and add the next code:
import {object, string} from "zod";
import {registerProvider} from "@/suppliers/register.supplier";
import axios from "axios";
export const DiscordProvider = registerProvider(
"discord",
{energetic: true},
object({
DISCORD_WEBHOOK_URL: string(),
}),
async (libName, stars, values) => {
await axios.publish(values.DISCORD_WEBHOOK_URL, {content material: `The library ${libName} has ${stars} new stars!`});
}
);
As you may see, we’re utilizing the registerProvider
to create a brand new supplier referred to as DiscordProvider
- We set the identify to
discord
- We set it to be energetic
- We specify that we’d like an surroundings variable referred to as
DISCORD_WEBHOOK_URL
. - We use a easy publish command with Axios so as to add the data to the test.
To get DISCORD_WEBHOOK_URL
:
- Go to your Discord server
- Click on edit on one of many channels
- Go to
Integrations
- Click on
Create Webhook
- Click on on the created webhook, then click on
Copy webhook URL
Edit our .env
file on our root undertaking and add
SLACK_WEBHOOK_URL=<your copied url>
2. Slack
Create a brand new file referred to as slack.supplier.ts
and add the next code:
import {object, string} from "zod";
import {registerProvider} from "@/suppliers/register.supplier";
import axios from "axios";
export const SlackProvider = registerProvider(
"slack",
{energetic: true},
object({
SLACK_WEBHOOK_URL: string(),
}),
async (libName, stars, values) => {
await axios.publish(values.SLACK_WEBHOOK_URL, {textual content: `The library ${libName} has ${stars} new stars!`});
}
);
As you may see, we’re utilizing the registerProvider
to create a brand new supplier referred to as SlackProvider
- We set the identify to
slack
- We set it to be energetic
- We specify that we’d like an surroundings variable referred to as
SLACK_WEBHOOK_URL
. - We use a easy publish command with Axios so as to add the data to the test.
To get SLACK_WEBHOOK_URL
:
- Create a brand new Slack app through the use of this URL: https://api.slack.com/apps?new_app=1
- Choose the primary choice: “From scratch”
- Give an app identify (any) and Slack the workspace you want to add the notifications too. Click on
Create App
. - In “Add options and functionalities,” click on
Incoming hook
- In Activate Incoming Webhooks, change it to “On”.
- Click on on “Add New Webhook to Workspace”.
- Choose the channel you need and click on “Permit”.
- Copy the webhook URL.
Edit our .env
file on our root undertaking and add
SLACK_WEBHOOK_URL=<your copied url>
3. Electronic mail
You should utilize completely different sorts of e-mail suppliers. For instance, we are going to use Resend to ship emails.
For that, let’s set up resend on our undertaking:
npm set up resend --save
Create a brand new file referred to as resend.supplier.ts
and add the next code:
import {object, string} from "zod";
import {registerProvider} from "@/suppliers/register.supplier";
import axios from "axios";
import { Resend } from 'resend';
export const ResendProvider = registerProvider(
"resend",
{energetic: true},
object({
RESEND_API_KEY: string(),
}),
async (libName, stars, values) => {
const resend = new Resend(values.RESEND_API_KEY);
await resend.emails.ship({
from: "Eric Allam <eric@set off.dev>",
to: ['eric@trigger.dev'],
topic: 'New GitHub stars',
html: `The library ${libName} has ${stars} new stars!`,
});
}
);
As you may see, we’re utilizing the registerProvider
to create a brand new supplier referred to as ResendProvider
- We set the identify to
resend
- We set it to be energetic
- We specify that we’d like an surroundings variable referred to as
RESEND_API_KEY
. - We use the Resend library to ship an e-mail to ourselves with the brand new variety of stars.
To get RESEND_API_KEY
:
- Create a brand new account at: https://resend.com
- Go to
API Keys
or use this URL https://resend.com/api-keys - Click on “+ Create API Key,” add the Key identify, select “Sending entry” and use the default “All Domains”. Click on Add.
- Copy the API Key.
Edit our .env
file on our root undertaking and add
RESEND_API_KEY=<your API key>
4. SMS
SMS are somewhat bit extra complicated as they require a number of variables.
For that, let’s set up Twilio on our undertaking:
npm set up twilio --save
Create a brand new file referred to as twilio.supplier.ts
and add the next code:
import {object, string} from "zod";
import {registerProvider} from "@/suppliers/register.supplier";
import axios from "axios";
import consumer from 'twilio';
export const TwilioProvider = registerProvider(
"twilio",
{energetic: true},
object({
TWILIO_SID: string(),
TWILIO_AUTH_TOKEN: string(),
TWILIO_FROM_NUMBER: string(),
TWILIO_TO_NUMBER: string(),
}),
async (libName, stars, values) => {
const twilio = consumer(values.TWILIO_SID, values.TWILIO_AUTH_TOKEN);
await twilio.messages.create({
physique: `The library ${libName} has ${stars} new stars!`,
from: values.TWILIO_FROM_NUMBER,
to: values.TWILIO_TO_NUMBER,
});
}
);
As you may see, we’re utilizing the registerProvider
to create a brand new supplier referred to as TwilioProvider
- We set the identify to
twilio
- We set it to be energetic
- We specify that we’d like surroundings variables:
TWILIO_SID
,TWILIO_AUTH_TOKEN
,TWILIO_FROM_NUMBER
andTWILIO_TO_NUMBER
- We use the Twilio
create
operate to ship an SMS.
To get TWILIO_SID
, TWILIO_AUTH_TOKEN
, TWILIO_FROM_NUMBER
and TWILIO_TO_NUMBER
- Create a brand new account at https://twilio.com
- Mark that you just wish to use it to ship SMSs.
- Click on “Get a telephone quantity”
- Copy the “Account SID”, “Auth Token” and “My Twilio Cellphone Quantity”
Edit our .env
file on our root undertaking and add
TWILIO_SID=<your SID key>
TWILIO_AUTH_TOKEN=<your AUTH TOKEN key>
TWILIO_FROM_NUMBER=<your FROM quantity>
TWILIO_TO_NUMBER=<your TO quantity>
Create new suppliers
As you may see, now it’s tremendous straightforward to create suppliers.
You can too use the open-source neighborhood to create new suppliers since they solely have to create one new file contained in the suppliers/record
listing.
The very last thing to do is edit your suppliers.ts
file and add all of your suppliers.
import {DiscordProvider} from "@/suppliers/record/discord.supplier";
import {ResendProvider} from "@/suppliers/record/resend.supplier";
import {SlackProvider} from "@/suppliers/record/slack.supplier";
import {TwilioProvider} from "@/suppliers/record/twilio.supplier";
export const Suppliers = [
DiscordProvider,
ResendProvider,
SlackProvider,
TwilioProvider,
];
Be at liberty to create extra suppliers for push notifications, net push notifications, in-app notifications, and so forth.
And you might be finished 🥳
Let’s join! 🔌
As an open-source developer, you are invited to affix our community to contribute and interact with maintainers. Do not hesitate to go to our GitHub repository to contribute and create points associated to Set off.dev.
The supply for this tutorial is out there right here:
https://github.com/triggerdotdev/blog/tree/main/stars-monitor-notifications
Thanks for studying!