mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-16 14:11:50 +00:00
starboard: add star count to embed, update automatically
This commit is contained in:
parent
5ec33848b4
commit
9883610a3b
6 changed files with 155 additions and 77 deletions
|
@ -2,6 +2,7 @@ import { starboardEvt } from "../types";
|
||||||
import { Message, TextChannel } from "eris";
|
import { Message, TextChannel } from "eris";
|
||||||
import { UnknownUser, resolveMember, noop, resolveUser } from "../../../utils";
|
import { UnknownUser, resolveMember, noop, resolveUser } from "../../../utils";
|
||||||
import { saveMessageToStarboard } from "../util/saveMessageToStarboard";
|
import { saveMessageToStarboard } from "../util/saveMessageToStarboard";
|
||||||
|
import { updateStarboardMessageStarCount } from "../util/updateStarboardMessageStarCount";
|
||||||
|
|
||||||
export const StarboardReactionAddEvt = starboardEvt({
|
export const StarboardReactionAddEvt = starboardEvt({
|
||||||
event: "messageReactionAdd",
|
event: "messageReactionAdd",
|
||||||
|
@ -59,21 +60,39 @@ export const StarboardReactionAddEvt = starboardEvt({
|
||||||
|
|
||||||
for (const starboard of applicableStarboards) {
|
for (const starboard of applicableStarboards) {
|
||||||
const boardLock = await pluginData.locks.acquire(`starboards-channel-${starboard.channel_id}`);
|
const boardLock = await pluginData.locks.acquire(`starboards-channel-${starboard.channel_id}`);
|
||||||
|
|
||||||
// Save reaction into the database
|
// Save reaction into the database
|
||||||
await pluginData.state.starboardReactions.createStarboardReaction(msg.id, userId).catch(noop);
|
await pluginData.state.starboardReactions.createStarboardReaction(msg.id, userId).catch(noop);
|
||||||
|
|
||||||
// If the message has already been posted to this starboard, we don't need to do anything else
|
const reactions = await pluginData.state.starboardReactions.getAllReactionsForMessageId(msg.id);
|
||||||
|
const reactionsCount = reactions.length;
|
||||||
|
|
||||||
const starboardMessages = await pluginData.state.starboardMessages.getMatchingStarboardMessages(
|
const starboardMessages = await pluginData.state.starboardMessages.getMatchingStarboardMessages(
|
||||||
starboard.channel_id,
|
starboard.channel_id,
|
||||||
msg.id,
|
msg.id,
|
||||||
);
|
);
|
||||||
if (starboardMessages.length > 0) continue;
|
if (starboardMessages.length > 0) {
|
||||||
|
// If the message has already been posted to this starboard, update star counts
|
||||||
const reactions = await pluginData.state.starboardReactions.getAllReactionsForMessageId(msg.id);
|
if (starboard.show_star_count) {
|
||||||
const reactionsCount = reactions.length;
|
for (const starboardMessage of starboardMessages) {
|
||||||
if (reactionsCount >= starboard.stars_required) {
|
const realStarboardMessage = await pluginData.client.getMessage(
|
||||||
|
starboardMessage.starboard_channel_id,
|
||||||
|
starboardMessage.starboard_message_id,
|
||||||
|
);
|
||||||
|
await updateStarboardMessageStarCount(
|
||||||
|
starboard,
|
||||||
|
msg,
|
||||||
|
realStarboardMessage,
|
||||||
|
starboard.star_emoji![0]!,
|
||||||
|
reactionsCount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (reactionsCount >= starboard.stars_required) {
|
||||||
|
// Otherwise, if the star count exceeds the required star count, save the message to the starboard
|
||||||
await saveMessageToStarboard(pluginData, msg, starboard);
|
await saveMessageToStarboard(pluginData, msg, starboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
boardLock.unlock();
|
boardLock.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ const StarboardOpts = t.type({
|
||||||
star_emoji: tNullable(t.array(t.string)),
|
star_emoji: tNullable(t.array(t.string)),
|
||||||
copy_full_embed: tNullable(t.boolean),
|
copy_full_embed: tNullable(t.boolean),
|
||||||
enabled: tNullable(t.boolean),
|
enabled: tNullable(t.boolean),
|
||||||
|
show_star_count: t.boolean,
|
||||||
});
|
});
|
||||||
export type TStarboardOpts = t.TypeOf<typeof StarboardOpts>;
|
export type TStarboardOpts = t.TypeOf<typeof StarboardOpts>;
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ export const PartialConfigSchema = tDeepPartial(ConfigSchema);
|
||||||
export const defaultStarboardOpts: Partial<TStarboardOpts> = {
|
export const defaultStarboardOpts: Partial<TStarboardOpts> = {
|
||||||
star_emoji: ["⭐"],
|
star_emoji: ["⭐"],
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
show_star_count: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface StarboardPluginType extends BasePluginType {
|
export interface StarboardPluginType extends BasePluginType {
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { EmbedWith, EMPTY_CHAR, messageLink } from "../../../utils";
|
||||||
|
import { EmbedOptions, GuildChannel, Message } from "eris";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const imageAttachmentExtensions = ["jpeg", "jpg", "png", "gif", "webp"];
|
||||||
|
const audioAttachmentExtensions = ["wav", "mp3", "m4a"];
|
||||||
|
const videoAttachmentExtensions = ["mp4", "mkv", "mov"];
|
||||||
|
|
||||||
|
type StarboardEmbed = EmbedWith<"footer" | "author" | "fields" | "timestamp">;
|
||||||
|
|
||||||
|
export function createStarboardEmbedFromMessage(msg: Message, copyFullEmbed: boolean): StarboardEmbed {
|
||||||
|
const embed: StarboardEmbed = {
|
||||||
|
footer: {
|
||||||
|
text: `#${(msg.channel as GuildChannel).name}`,
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
name: `${msg.author.username}#${msg.author.discriminator}`,
|
||||||
|
},
|
||||||
|
fields: [],
|
||||||
|
timestamp: new Date(msg.timestamp).toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (msg.author.avatarURL) {
|
||||||
|
embed.author.icon_url = msg.author.avatarURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The second condition here checks for messages with only an image link that is then embedded.
|
||||||
|
// The message content in that case is hidden by the Discord client, so we hide it here too.
|
||||||
|
if (msg.content && msg.embeds[0]?.thumbnail?.url !== msg.content) {
|
||||||
|
embed.description = msg.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge media and - if copy_full_embed is enabled - fields and title from the first embed in the original message
|
||||||
|
if (msg.embeds.length > 0) {
|
||||||
|
if (msg.embeds[0].image) {
|
||||||
|
embed.image = msg.embeds[0].image;
|
||||||
|
} else if (msg.embeds[0].thumbnail) {
|
||||||
|
embed.image = { url: msg.embeds[0].thumbnail.url };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copyFullEmbed) {
|
||||||
|
if (msg.embeds[0].title) {
|
||||||
|
const titleText = msg.embeds[0].url ? `[${msg.embeds[0].title}](${msg.embeds[0].url})` : msg.embeds[0].title;
|
||||||
|
embed.fields.push({ name: EMPTY_CHAR, value: titleText });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.embeds[0].fields) {
|
||||||
|
embed.fields.push(...msg.embeds[0].fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no embeds, add the first image attachment explicitly
|
||||||
|
else if (msg.attachments.length) {
|
||||||
|
for (const attachment of msg.attachments) {
|
||||||
|
const ext = path
|
||||||
|
.extname(attachment.filename)
|
||||||
|
.slice(1)
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
if (imageAttachmentExtensions.includes(ext)) {
|
||||||
|
embed.image = { url: attachment.url };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioAttachmentExtensions.includes(ext)) {
|
||||||
|
embed.fields.push({ name: EMPTY_CHAR, value: `*Message contains an audio clip*` });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoAttachmentExtensions.includes(ext)) {
|
||||||
|
embed.fields.push({ name: EMPTY_CHAR, value: `*Message contains a video*` });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { EmbedField, EmojiOptions, GuildChannel, Message } from "eris";
|
||||||
|
import { EMPTY_CHAR, messageLink } from "../../../utils";
|
||||||
|
import { TStarboardOpts } from "../types";
|
||||||
|
|
||||||
|
export function createStarboardPseudoFooterForMessage(
|
||||||
|
starboard: TStarboardOpts,
|
||||||
|
msg: Message,
|
||||||
|
starEmoji: string,
|
||||||
|
starCount: number,
|
||||||
|
): EmbedField {
|
||||||
|
const jumpLink = `[Jump to message](${messageLink(msg)})`;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
if (starboard.show_star_count) {
|
||||||
|
content =
|
||||||
|
starCount > 1
|
||||||
|
? `${starEmoji} **${starCount}** \u200B \u200B \u200B ${jumpLink}`
|
||||||
|
: `${starEmoji} \u200B ${jumpLink}`;
|
||||||
|
} else {
|
||||||
|
content = jumpLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: EMPTY_CHAR,
|
||||||
|
value: content,
|
||||||
|
};
|
||||||
|
}
|
|
@ -4,10 +4,8 @@ import { Message, GuildChannel, TextChannel, Embed } from "eris";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { EmbedWith, EMPTY_CHAR, messageLink } from "../../../utils";
|
import { EmbedWith, EMPTY_CHAR, messageLink } from "../../../utils";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { createStarboardEmbedFromMessage } from "./createStarboardEmbedFromMessage";
|
||||||
const imageAttachmentExtensions = ["jpeg", "jpg", "png", "gif", "webp"];
|
import { createStarboardPseudoFooterForMessage } from "./createStarboardPseudoFooterForMessage";
|
||||||
const audioAttachmentExtensions = ["wav", "mp3", "m4a"];
|
|
||||||
const videoAttachmentExtensions = ["mp4", "mkv", "mov"];
|
|
||||||
|
|
||||||
export async function saveMessageToStarboard(
|
export async function saveMessageToStarboard(
|
||||||
pluginData: GuildPluginData<StarboardPluginType>,
|
pluginData: GuildPluginData<StarboardPluginType>,
|
||||||
|
@ -17,73 +15,9 @@ export async function saveMessageToStarboard(
|
||||||
const channel = pluginData.guild.channels.get(starboard.channel_id);
|
const channel = pluginData.guild.channels.get(starboard.channel_id);
|
||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
|
|
||||||
const embed: EmbedWith<"footer" | "author" | "fields" | "timestamp"> = {
|
const starCount = (await pluginData.state.starboardReactions.getAllReactionsForMessageId(msg.id)).length;
|
||||||
footer: {
|
const embed = createStarboardEmbedFromMessage(msg, Boolean(starboard.copy_full_embed));
|
||||||
text: `#${(msg.channel as GuildChannel).name}`,
|
embed.fields!.push(createStarboardPseudoFooterForMessage(starboard, msg, starboard.star_emoji![0], starCount));
|
||||||
},
|
|
||||||
author: {
|
|
||||||
name: `${msg.author.username}#${msg.author.discriminator}`,
|
|
||||||
},
|
|
||||||
fields: [],
|
|
||||||
timestamp: new Date(msg.timestamp).toISOString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (msg.author.avatarURL) {
|
|
||||||
embed.author.icon_url = msg.author.avatarURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The second condition here checks for messages with only an image link that is then embedded.
|
|
||||||
// The message content in that case is hidden by the Discord client, so we hide it here too.
|
|
||||||
if (msg.content && msg.embeds[0]?.thumbnail?.url !== msg.content) {
|
|
||||||
embed.description = msg.content;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge media and - if copy_full_embed is enabled - fields and title from the first embed in the original message
|
|
||||||
if (msg.embeds.length > 0) {
|
|
||||||
if (msg.embeds[0].image) {
|
|
||||||
embed.image = msg.embeds[0].image;
|
|
||||||
} else if (msg.embeds[0].thumbnail) {
|
|
||||||
embed.image = { url: msg.embeds[0].thumbnail.url };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (starboard.copy_full_embed) {
|
|
||||||
if (msg.embeds[0].title) {
|
|
||||||
const titleText = msg.embeds[0].url ? `[${msg.embeds[0].title}](${msg.embeds[0].url})` : msg.embeds[0].title;
|
|
||||||
embed.fields.push({ name: EMPTY_CHAR, value: titleText });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.embeds[0].fields) {
|
|
||||||
embed.fields.push(...msg.embeds[0].fields);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no embeds, add the first image attachment explicitly
|
|
||||||
else if (msg.attachments.length) {
|
|
||||||
for (const attachment of msg.attachments) {
|
|
||||||
const ext = path
|
|
||||||
.extname(attachment.filename)
|
|
||||||
.slice(1)
|
|
||||||
.toLowerCase();
|
|
||||||
|
|
||||||
if (imageAttachmentExtensions.includes(ext)) {
|
|
||||||
embed.image = { url: attachment.url };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audioAttachmentExtensions.includes(ext)) {
|
|
||||||
embed.fields.push({ name: EMPTY_CHAR, value: `*Message contains an audio clip*` });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoAttachmentExtensions.includes(ext)) {
|
|
||||||
embed.fields.push({ name: EMPTY_CHAR, value: `*Message contains a video*` });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
embed.fields.push({ name: EMPTY_CHAR, value: `[Jump to message](${messageLink(msg)})` });
|
|
||||||
|
|
||||||
const starboardMessage = await (channel as TextChannel).createMessage({ embed });
|
const starboardMessage = await (channel as TextChannel).createMessage({ embed });
|
||||||
await pluginData.state.starboardMessages.createStarboardMessage(channel.id, msg.id, starboardMessage.id);
|
await pluginData.state.starboardMessages.createStarboardMessage(channel.id, msg.id, starboardMessage.id);
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Client, GuildTextableChannel, Message } from "eris";
|
||||||
|
import { noop } from "../../../utils";
|
||||||
|
import { createStarboardPseudoFooterForMessage } from "./createStarboardPseudoFooterForMessage";
|
||||||
|
import { TStarboardOpts } from "../types";
|
||||||
|
|
||||||
|
export async function updateStarboardMessageStarCount(
|
||||||
|
starboard: TStarboardOpts,
|
||||||
|
originalMessage: Message,
|
||||||
|
starboardMessage: Message,
|
||||||
|
starEmoji: string,
|
||||||
|
starCount: number,
|
||||||
|
) {
|
||||||
|
const embed = starboardMessage.embeds[0]!;
|
||||||
|
embed.fields!.shift(); // Remove pseudo footer
|
||||||
|
embed.fields!.push(createStarboardPseudoFooterForMessage(starboard, originalMessage, starEmoji, starCount)); // Create new pseudo footer
|
||||||
|
await starboardMessage.edit({ embed });
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue