Skip to content

Commit

Permalink
feat: pr, issues, discussions and releases webhook handling (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldietzler authored Oct 2, 2024
1 parent 3e4abb7 commit 6de959d
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 7 deletions.
101 changes: 101 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,104 @@ export const HELP_TEXTS = {
'reverse proxy': `This sounds like it could be a reverse proxy issue. Here's a link to the relevant documentation page: ${Constants.Urls.Docs.ReverseProxy}.`,
'feature request': `For ideas or features you'd like Immich to have, feel free to [open a feature request in the Github discussions](${Constants.Urls.FeatureRequest}). However, please make sure to search for similar requests first to avoid duplicates.`,
};

export const ReleaseMessages = [
'A day with a release is a good day!',
'New release, new possibilities!',
"It's release day! Say hello to the latest version of Immich.",
'🎉 Release time! 🚀',
'Time to update! Immich just got better.',
'Immich vX.Y is live!',
"Our new release is here, and it's worth the wait.",
"Out of the oven, fresh and new: Immich's latest release.",
'Break out the confetti — Immich has released a new version!',
'Guess what? New release, same awesomeness!',
"Ready to upgrade? Immich's latest version just dropped.",
'Release alert! Immich has leveled up.',
"🚨 Breaking news: We found bugs, squashed them, and now they're an endangered species. New release out!",
"We released a new version! It's like the last one but with fewer mistakes.",
"Guess who's back? Back again. Immich's back. Tell a friend.",
"If this release were a pizza, it'd have extra cheese, no bugs, and a side of perfection. 🍕",
"New release? You bet! And no, we didn't just hit CTRL+C, CTRL+V this time!",
'We released a new version! The bugs cried, the features cheered, and the code sighed in relief.',
"New Immich version out! It's like the old version, but with 100% more awesome.",
'We released so much cool stuff, our code is wearing sunglasses now. 😎',
'Immich release: because you deserve better, and our code needed therapy.',
'This release is smoother than your morning coffee — without the jitters! ☕',
'Our latest release is like a cat — it fixed bugs and then knocked everything else off the table. 🐱',
"We found bugs. We squashed them. Then we found more. It's an endless cycle, but here's the latest release!",
"New release dropping like it's hot — because it is! 🔥",
"Released: A version so fresh, even the bugs didn't see it coming.",
'Update your Immich before the bugs start planning their revenge.',
'New Immich release: like finding a $20 bill in your old jacket.',
'We fixed the bugs, upgraded the features, and now the code is doing a happy dance.',
"It's release day! Prepare for fewer bugs and more hugs. 🤗",
'The new Immich release is so good, even our test scripts are celebrating. 🎉',
"We just released the new version! You'll feel like a coding superhero with these features. 🦸",
'New release: Now with extra polish, like the fancy shoes you wear once a year.',
"Forget the gym — we've been squashing bugs for cardio. New release out now!",
"New version out! We're officially better at fixing bugs than your morning alarm is at waking you up.",
'Our code had a glow-up! Check out the newest version of Immich. ✨',
"Introducing the new release: It's like a magic trick, but instead of a rabbit, we pulled out new features. 🎩",
"The new Immich release is here, and it's cooler than a penguin in sunglasses. 🐧😎",
"New release alert! We found bugs, called in the exterminators, and now it's clean code paradise.",
"This release is so smooth, we're thinking of entering it into the Winter Olympics. ⛷️",
'The new version is here — just in time to rescue your project from whatever “version X” was doing.',
'New Immich release: now with 75% more feature magic and 100% fewer “oops” moments.',
'Code ninjas at work! Bugs have been stealthily eliminated in this release. 🥷',
"Our new release is out, and let's just say: our code is finally having a good hair day.",
'Immich release day: More fixes than a coffee addict in a caffeine shop. ☕',
'New release: Now with 50% more code confidence and 100% fewer error messages.',
"Our latest release is like a fine wine: it took some time, but it's finally perfect! 🍷",
"Our new release is live! It's so optimized, even your grandma's computer will run it.",
'Update or be left behind... with all the bugs. 🐛',
'New Immich release: because who needs sleep when you have flawless code?',
'Introducing the new version, powered by coffee and the fear of deadlines.',
'Release day: where bugs go to disappear and features come to life.',
'Our latest release is the code equivalent of a spa day — fresh, clean, and relaxed.',
'New release is here, no cap! Unless you left Caps Lock on. Then, maybe.',
'This release is like a hug from a cat — unexpected but very welcome. 🐾',
'Just dropped a new release! Now featuring 100% fewer excuses.',
'Get ready for the smoothest Immich release yet — like butter on a hot pancake. 🥞',
"Release day! We promise it's better than that thing you forgot in the microwave.",
'New release: Now with 50% more “wow” and 100% less “huh?”.',
"The code is so clean now, you could eat off it. But please don't.",
"Version upgrade! Because “Ctrl+Alt+Delete” shouldn't be your default solution.",
"We've leveled up! This new release is practically a cheat code.",
"Immich update: the only time you'll actually enjoy being bugged for an update.",
'New release is here! Our code finally stopped rebelling.',
'We tried to add a dancing unicorn, but settled for bug fixes instead. 🦄',
'Guess what? The new release is shinier than a freshly polished keyboard.',
'New release: like a fine cup of coffee, but for your code.',
"Immich update: It's like getting a surprise pizza, but digital.",
'Our latest version is out! Cleaner code, happier developers.',
'Bug fixes were hard, but we made it look easy. New release is here!',
"Releasing the latest version: It's sleeker than your favorite pair of sneakers.",
'This release is so good, we almost threw a party. 🎉',
"New Immich release: Because you deserve software that doesn't make you cry.",
'Release day: We put the bugs on notice and shipped greatness.',
"This release is smoother than that cool breeze you're wishing for.",
'New version alert: Ready to make your life 42% better. 📈',
'Immich release: where coding mistakes go to retire.',
'Update now! Our bugs are out of a job.',
'New version out! Now with 100% more stability and 200% more awesome.',
'We released so many features, we forgot to count.',
'Latest release: all the bug fixes, none of the headaches.',
"It's release time! Your code just became 10x cooler.",
'Immich update: fresh as a morning breeze (but no alarms required).',
'Our latest release is cooler than a cucumber. 🥒',
'Upgrade now! We fixed all the things, and maybe added a few surprises.',
'New release: Because bug-free is the way to be.',
"Brace yourself! The new version of Immich is here, and it's magnificent.",
'Release day: Code so clean, even Marie Kondo approves.',
'Our latest release is hotter than your favorite meme right now.',
"Goodbye bugs, hello perfection! Immich's new release is live.",
'The only thing more exciting than this release? Your reaction when you see it.',
"The new version is out! You didn't know you needed it, but trust us — you do.",
'Immich release: Your code will thank you later.',
'New version out! Even the bugs are scared to come back.',
"Our code just went from 'meh' to 'heck yeah!' with this release.",
'Release day: Just click update and let the magic happen.',
"Update time! New version, same awesome you've come to expect.",
"Immich just released a new version, and trust us — it's worth a celebration!",
];
3 changes: 3 additions & 0 deletions src/interfaces/discord.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export enum DiscordChannel {
Stripe = '1263492970691297300',
SupportCrew = '1184258493948117084',
QQ = '1157429449671856148',
PullRequests = '991483093179445350',
IssuesAndDiscussions = '991483015958106202',
Releases = '991477056791658567',
}

export enum DiscordEvents {
Expand Down
124 changes: 117 additions & 7 deletions src/services/webhook.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import { WebhookEvent } from '@octokit/webhooks-types';
import { User, WebhookEvent } from '@octokit/webhooks-types';
import { Colors, EmbedBuilder, MessageFlags } from 'discord.js';
import _ from 'lodash';
import { getConfig } from 'src/config';
import { Constants } from 'src/constants';
import { Constants, ReleaseMessages } from 'src/constants';
import { GithubStatusComponent, GithubStatusIncident, PaymentIntent, StripeBase } from 'src/dtos/webhook.dto';
import { IDatabaseRepository } from 'src/interfaces/database.interface';
import { DiscordChannel, IDiscordInterface } from 'src/interfaces/discord.interface';
Expand All @@ -19,6 +20,14 @@ const isPaymentEvent = (payload: StripeBase): payload is StripeBase<PaymentInten
const isImmichProduct = (payload: StripeBase<PaymentIntent>) =>
['immich-server', 'immich-client'].includes(payload.data.object.description);

type BaseEvent = {
number: number;
title: string;
user: User;
html_url: string;
body: string | null;
};

@Injectable()
export class WebhookService {
private logger = new Logger(WebhookService.name);
Expand All @@ -39,12 +48,50 @@ export class WebhookService {
return;
}

if ('release' in dto && dto.action === 'released') {
await this.zulip.sendMessage({
stream: Constants.Zulip.Streams.Immich,
topic: Constants.Zulip.Topics.ImmichRelease,
content: `A day with a release is a good day! ${dto.release.html_url} 🚀`,
const { action } = dto;

if (
'pull_request' in dto &&
(action === 'opened' || action === 'closed' || action === 'converted_to_draft' || action === 'ready_for_review')
) {
const embed = this.getEmbed(action, dto.repository.full_name, 'Pull request', dto.pull_request);
const color = this.getPrEmbedColor({
action,
isDraft: dto.pull_request.draft,
isMerged: dto.pull_request.merged,
});
embed.setColor(color);

await this.discord.sendMessage(DiscordChannel.PullRequests, { embeds: [embed] });
return;
}

if ('issue' in dto && (action === 'opened' || action === 'closed')) {
const embed = this.getEmbed(action, dto.repository.full_name, 'Issue', dto.issue);
embed.setColor(this.getIssueEmbedColor({ action }));

await this.discord.sendMessage(DiscordChannel.IssuesAndDiscussions, { embeds: [embed] });
return;
}

if ('discussion' in dto && (action === 'created' || action === 'deleted' || action === 'answered')) {
const embed = this.getEmbed(action, dto.repository.full_name, 'Issue', dto.discussion);
embed.setColor(this.getDiscussionEmbedColor({ action }));

await this.discord.sendMessage(DiscordChannel.IssuesAndDiscussions, { embeds: [embed] });
return;
}

if ('release' in dto && action === 'released') {
const content = `${_.sample(ReleaseMessages)} ${dto.release.html_url}`;
await Promise.all([
this.zulip.sendMessage({
stream: Constants.Zulip.Streams.Immich,
topic: Constants.Zulip.Topics.ImmichRelease,
content,
}),
this.discord.sendMessage(DiscordChannel.Releases, { content, flags: [MessageFlags.SuppressEmbeds] }),
]);
}
}

Expand Down Expand Up @@ -142,4 +189,67 @@ export class WebhookService {
flags: [MessageFlags.SuppressNotifications],
});
}

private getEmbed(action: string, repositoryName: string, title: string, event: BaseEvent) {
return new EmbedBuilder({
title: `[${repositoryName}] ${title} ${action}: #${event.number} ${event.title}`,
author: {
name: event.user.login,
url: event.user.html_url,
iconURL: event.user.avatar_url,
},
url: event.html_url,
description: action === 'opened' || action === 'created' ? (event.body ?? undefined) : undefined,
});
}

private getPrEmbedColor(dto: {
action: 'opened' | 'closed' | 'converted_to_draft' | 'ready_for_review';
isDraft: boolean;
isMerged: boolean | null;
}) {
switch (dto.action) {
case 'opened': {
return dto.isDraft ? 'Grey' : 'Green';
}
case 'closed': {
if (dto.isMerged === null) {
this.logger.error('Closed PR should have isMerged set.');
return null;
}
return dto.isMerged ? 'Purple' : 'Red';
}
case 'converted_to_draft': {
return 'Grey';
}
case 'ready_for_review': {
return 'Green';
}
}
}

private getIssueEmbedColor(dto: { action: 'opened' | 'closed' }) {
switch (dto.action) {
case 'opened': {
return 'Green';
}
case 'closed': {
return 'NotQuiteBlack';
}
}
}

private getDiscussionEmbedColor(dto: { action: 'created' | 'deleted' | 'answered' }) {
switch (dto.action) {
case 'created': {
return 'Orange';
}
case 'deleted': {
return 'NotQuiteBlack';
}
case 'answered': {
return 'Green';
}
}
}
}

0 comments on commit 6de959d

Please sign in to comment.