This commit is contained in:
@@ -1,4 +1,18 @@
|
||||
import { Client, SlashCommandBuilder } from "discord.js";
|
||||
import {
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
Client,
|
||||
ContainerBuilder,
|
||||
MediaGalleryBuilder,
|
||||
MediaGalleryItemBuilder,
|
||||
MessageFlags,
|
||||
SectionBuilder,
|
||||
SeparatorBuilder,
|
||||
SeparatorSpacingSize,
|
||||
SlashCommandBuilder,
|
||||
TextDisplayBuilder,
|
||||
ThumbnailBuilder,
|
||||
} from "discord.js";
|
||||
import { registerCommand } from "../lib/commandRegistry";
|
||||
import { CHANNELS, ROLES } from "../config";
|
||||
import { db } from "..";
|
||||
@@ -89,8 +103,41 @@ export default async function (_client: Client) {
|
||||
// Post Process Content
|
||||
content = content.replace(/\\n/g, "\n"); // allow users to input \n for newlines
|
||||
|
||||
const fields = [];
|
||||
// Build Components V2 container
|
||||
const container = new ContainerBuilder().setAccentColor(
|
||||
0x5865f2,
|
||||
);
|
||||
|
||||
// Optional image as media gallery at the top
|
||||
if (image) {
|
||||
container.addMediaGalleryComponents(
|
||||
new MediaGalleryBuilder().addItems(
|
||||
new MediaGalleryItemBuilder().setURL(image.url),
|
||||
),
|
||||
);
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(true)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
}
|
||||
|
||||
// Title
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(`## 📢 ${title}`),
|
||||
);
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(true)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
|
||||
// Content
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(content),
|
||||
);
|
||||
|
||||
// Changes
|
||||
if (changesRaw && changesRaw.trim() !== "") {
|
||||
const changeLines = changesRaw
|
||||
.split(",")
|
||||
@@ -98,22 +145,25 @@ export default async function (_client: Client) {
|
||||
.filter((c) => c.length > 0)
|
||||
.map((c) => `• ${c}`)
|
||||
.join("\n");
|
||||
fields.push({
|
||||
name: "📋 Changes",
|
||||
value: changeLines,
|
||||
inline: false,
|
||||
});
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`**📋 Changes**\n${changeLines}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Releasing
|
||||
if (comingWhenRaw && comingWhenRaw.trim() !== "") {
|
||||
let epoch: number | null = null;
|
||||
const trimmed = comingWhenRaw.trim();
|
||||
let releasingValue: string;
|
||||
if (trimmed.toLowerCase() === "soon") {
|
||||
fields.push({
|
||||
name: "📅 Releasing",
|
||||
value: "Soon™",
|
||||
inline: false,
|
||||
});
|
||||
releasingValue = "Soon™";
|
||||
} else {
|
||||
// Try DD/MM/YYYY
|
||||
const ddmmyyyy = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(
|
||||
@@ -129,42 +179,67 @@ export default async function (_client: Client) {
|
||||
if (!isNaN(date.getTime()))
|
||||
epoch = Math.floor(date.getTime() / 1000);
|
||||
} else if (/^\d+$/.test(trimmed)) {
|
||||
// Raw epoch
|
||||
epoch = Number(trimmed);
|
||||
}
|
||||
if (epoch !== null) {
|
||||
fields.push({
|
||||
name: "📅 Releasing",
|
||||
value: `<t:${epoch}:F> (<t:${epoch}:R>)`,
|
||||
inline: false,
|
||||
});
|
||||
} else {
|
||||
fields.push({
|
||||
name: "📅 Releasing",
|
||||
value: trimmed,
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
releasingValue =
|
||||
epoch !== null
|
||||
? `<t:${epoch}:F> (<t:${epoch}:R>)`
|
||||
: trimmed;
|
||||
}
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`**📅 Releasing**\n${releasingValue}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
fields.push({
|
||||
name: "🔗 More Info",
|
||||
value: `[Click here](${link})`,
|
||||
inline: false,
|
||||
});
|
||||
// More Info button section
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(true)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addSectionComponents(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
"🔗 **More Info**",
|
||||
),
|
||||
)
|
||||
.setButtonAccessory(
|
||||
new ButtonBuilder()
|
||||
.setLabel("Click here")
|
||||
.setURL(link)
|
||||
.setStyle(ButtonStyle.Link),
|
||||
),
|
||||
);
|
||||
|
||||
const announcementEmbed = {
|
||||
title: `📢 ${title}`,
|
||||
description: content,
|
||||
color: 0x5865f2,
|
||||
fields,
|
||||
footer: {
|
||||
text: `Announced by ${interaction.user.username}`,
|
||||
icon_url: interaction.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
// Footer
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addSectionComponents(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`-# Announced by ${interaction.user.username}`,
|
||||
),
|
||||
)
|
||||
.setThumbnailAccessory(
|
||||
new ThumbnailBuilder({
|
||||
media: {
|
||||
url: interaction.user.displayAvatarURL(),
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
// Send the announcement to a specific channel
|
||||
const announcementChannel =
|
||||
@@ -172,11 +247,9 @@ export default async function (_client: Client) {
|
||||
CHANNELS.ANNOUNCEMENTS,
|
||||
);
|
||||
if (announcementChannel?.isTextBased()) {
|
||||
if (image) {
|
||||
await announcementChannel.send({ files: [image.url] });
|
||||
}
|
||||
const message = await announcementChannel.send({
|
||||
embeds: [announcementEmbed],
|
||||
flags: MessageFlags.IsComponentsV2,
|
||||
components: [container],
|
||||
});
|
||||
|
||||
// Reactions for engagement
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
import { Client, SlashCommandBuilder } from "discord.js";
|
||||
import {
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
Client,
|
||||
ContainerBuilder,
|
||||
MessageFlags,
|
||||
SectionBuilder,
|
||||
SeparatorBuilder,
|
||||
SeparatorSpacingSize,
|
||||
SlashCommandBuilder,
|
||||
TextDisplayBuilder,
|
||||
ThumbnailBuilder,
|
||||
} from "discord.js";
|
||||
import { registerCommand } from "../lib/commandRegistry";
|
||||
import { CHANNELS, ROLES } from "../config";
|
||||
import { db } from "..";
|
||||
@@ -109,8 +121,25 @@ export default async function (_client: Client) {
|
||||
// Post Process Content
|
||||
newContent = newContent.replace(/\\n/g, "\n"); // allow users to input \n for newlines
|
||||
|
||||
const fields = [];
|
||||
// Build Components V2 container
|
||||
const container = new ContainerBuilder().setAccentColor(0x5865f2);
|
||||
|
||||
// Title
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(`## 📢 ${newTitle}`),
|
||||
);
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(true)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
|
||||
// Content
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(newContent),
|
||||
);
|
||||
|
||||
// Changes
|
||||
if (changesRaw && changesRaw.trim() !== "") {
|
||||
const changeLines = changesRaw
|
||||
.split(",")
|
||||
@@ -118,22 +147,25 @@ export default async function (_client: Client) {
|
||||
.filter((c: string) => c.length > 0)
|
||||
.map((c: string) => `• ${c}`)
|
||||
.join("\n");
|
||||
fields.push({
|
||||
name: "📋 Changes",
|
||||
value: changeLines,
|
||||
inline: false,
|
||||
});
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`**📋 Changes**\n${changeLines}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Releasing
|
||||
if (comingWhenRaw && comingWhenRaw.trim() !== "") {
|
||||
let epoch: number | null = null;
|
||||
const trimmed = comingWhenRaw.trim();
|
||||
let releasingValue: string;
|
||||
if (trimmed.toLowerCase() === "soon") {
|
||||
fields.push({
|
||||
name: "📅 Releasing",
|
||||
value: "Soon™",
|
||||
inline: false,
|
||||
});
|
||||
releasingValue = "Soon™";
|
||||
} else {
|
||||
const ddmmyyyy = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(
|
||||
trimmed,
|
||||
@@ -150,39 +182,64 @@ export default async function (_client: Client) {
|
||||
} else if (/^\d+$/.test(trimmed)) {
|
||||
epoch = Number(trimmed);
|
||||
}
|
||||
if (epoch !== null) {
|
||||
fields.push({
|
||||
name: "📅 Releasing",
|
||||
value: `<t:${epoch}:F> (<t:${epoch}:R>)`,
|
||||
inline: false,
|
||||
});
|
||||
} else {
|
||||
fields.push({
|
||||
name: "📅 Releasing",
|
||||
value: trimmed,
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
releasingValue =
|
||||
epoch !== null
|
||||
? `<t:${epoch}:F> (<t:${epoch}:R>)`
|
||||
: trimmed;
|
||||
}
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`**📅 Releasing**\n${releasingValue}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
fields.push({
|
||||
name: "🔗 More Info",
|
||||
value: `[Click here](${link})`,
|
||||
inline: false,
|
||||
});
|
||||
// More Info button section
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(true)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addSectionComponents(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent("🔗 **More Info**"),
|
||||
)
|
||||
.setButtonAccessory(
|
||||
new ButtonBuilder()
|
||||
.setLabel("Click here")
|
||||
.setURL(link)
|
||||
.setStyle(ButtonStyle.Link),
|
||||
),
|
||||
);
|
||||
|
||||
const updatedEmbed = {
|
||||
title: `📢 ${newTitle}`,
|
||||
description: newContent,
|
||||
color: 0x5865f2,
|
||||
fields,
|
||||
footer: {
|
||||
text: `Announced by ${existing.announcedByUsername ?? interaction.user.username} • Last edited by ${interaction.user.username}`,
|
||||
icon_url: interaction.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
// Footer
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addSectionComponents(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`-# Announced by ${
|
||||
existing.announcedByUsername ??
|
||||
interaction.user.username
|
||||
} • Last edited by ${interaction.user.username}`,
|
||||
),
|
||||
)
|
||||
.setThumbnailAccessory(
|
||||
new ThumbnailBuilder({
|
||||
media: { url: interaction.user.displayAvatarURL() },
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
// Fetch and edit the original message
|
||||
const announcementChannel = interaction.guild?.channels.cache.get(
|
||||
@@ -208,7 +265,10 @@ export default async function (_client: Client) {
|
||||
return;
|
||||
}
|
||||
|
||||
await targetMessage.edit({ embeds: [updatedEmbed] });
|
||||
await targetMessage.edit({
|
||||
flags: MessageFlags.IsComponentsV2,
|
||||
components: [container],
|
||||
});
|
||||
|
||||
// Update DB
|
||||
await db.collection("announcements").updateOne(
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import { Express, Request, Response } from "express";
|
||||
import { EmbedBuilder, TextChannel } from "discord.js";
|
||||
import {
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ContainerBuilder,
|
||||
MessageFlags,
|
||||
SectionBuilder,
|
||||
SeparatorBuilder,
|
||||
SeparatorSpacingSize,
|
||||
TextChannel,
|
||||
TextDisplayBuilder,
|
||||
ThumbnailBuilder,
|
||||
} from "discord.js";
|
||||
import { CHANNELS } from "../config";
|
||||
import { client, db } from "../index";
|
||||
|
||||
@@ -17,12 +28,10 @@ export default async function gitCommitPOST(app: Express) {
|
||||
}
|
||||
|
||||
if (event !== "push") {
|
||||
return res
|
||||
.status(200)
|
||||
.json({
|
||||
success: true,
|
||||
message: `Event '${event}' ignored`,
|
||||
});
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: `Event '${event}' ignored`,
|
||||
});
|
||||
}
|
||||
|
||||
const body = req.body;
|
||||
@@ -51,32 +60,87 @@ export default async function gitCommitPOST(app: Express) {
|
||||
commitLines.push(`...and ${commits.length - SHOW_MAX} more`);
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(forced ? 0xff4444 : 0x2ea44f)
|
||||
.setTitle(
|
||||
`${forced ? "⚠️ Force Push" : "📦 New Push"} to \`${branch}\``,
|
||||
)
|
||||
.setURL(compareUrl)
|
||||
.setAuthor({
|
||||
name: pusher.name,
|
||||
iconURL: `https://github.com/${pusher.name}.png`,
|
||||
url: `https://github.com/${pusher.name}`,
|
||||
})
|
||||
.addFields(
|
||||
{ name: "🌿 Branch", value: `\`${branch}\``, inline: true },
|
||||
{
|
||||
name: `📝 Commits (${commits.length})`,
|
||||
value: commitLines.join("\n") || "_No commits_",
|
||||
},
|
||||
)
|
||||
.setFooter({
|
||||
text: `Delivery: ${req.headers["x-github-delivery"] ?? "unknown"}`,
|
||||
})
|
||||
.setTimestamp(
|
||||
headCommit.timestamp
|
||||
? new Date(headCommit.timestamp)
|
||||
: new Date(),
|
||||
const container = new ContainerBuilder().setAccentColor(
|
||||
forced ? 0xff4444 : 0x2ea44f,
|
||||
);
|
||||
|
||||
// Author + title section
|
||||
container.addSectionComponents(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`## ${forced ? "⚠️ Force Push" : "📦 New Push"} to \`${branch}\``,
|
||||
),
|
||||
)
|
||||
.setThumbnailAccessory(
|
||||
new ThumbnailBuilder({
|
||||
media: {
|
||||
url: `https://github.com/${pusher.name}.png`,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(true)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
|
||||
// Branch & commits
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`**🌿 Branch:** \`${branch}\``,
|
||||
),
|
||||
);
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`**📝 Commits (${commits.length})**\n${
|
||||
commitLines.join("\n") || "_No commits_"
|
||||
}`,
|
||||
),
|
||||
);
|
||||
|
||||
// Compare link button (only if a URL is available)
|
||||
if (compareUrl) {
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(true)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addSectionComponents(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
"🔗 **View Changes**",
|
||||
),
|
||||
)
|
||||
.setButtonAccessory(
|
||||
new ButtonBuilder()
|
||||
.setLabel("Compare")
|
||||
.setURL(compareUrl)
|
||||
.setStyle(ButtonStyle.Link),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Footer
|
||||
container.addSeparatorComponents(
|
||||
new SeparatorBuilder()
|
||||
.setDivider(false)
|
||||
.setSpacing(SeparatorSpacingSize.Small),
|
||||
);
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
`-# Delivery: ${
|
||||
req.headers["x-github-delivery"] ?? "unknown"
|
||||
}`,
|
||||
),
|
||||
);
|
||||
|
||||
const channel = (await client.channels.fetch(
|
||||
configured_channel,
|
||||
@@ -90,7 +154,10 @@ export default async function gitCommitPOST(app: Express) {
|
||||
.json({ error: "Discord channel unavailable" });
|
||||
}
|
||||
|
||||
const message = await channel.send({ embeds: [embed] });
|
||||
const message = await channel.send({
|
||||
flags: MessageFlags.IsComponentsV2,
|
||||
components: [container],
|
||||
});
|
||||
console.log(
|
||||
`[WEB-gitCommit] Push event sent to configured channel (${commits.length} commits on ${branch})`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user