Discord-InterChat/InterChat

View on GitHub
src/utils/GuildUtils.ts

Summary

Maintainability
A
0 mins
Test Coverage
import Constants, { mascotEmojis } from '#utils/Constants.js';
import { stripIndents } from 'common-tags';
import {
  AuditLogEvent,
  ChannelType,
  EmbedBuilder,
  type ColorResolvable,
  type Guild,
  type TextChannel,
} from 'discord.js';

/**
 * Retrieves the first channel in a guild or the inviter of the bot.
 * @param guild The guild to retrieve the target for.
 * @returns The greeting target, which can be a TextChannel or a User.
 */
export const getGuildOwnerOrFirstChannel = async (guild: Guild) => {
  let guildOwner = null;

  if (guild.members.me?.permissions.has('ViewAuditLog', true)) {
    const auditLog = await guild
      .fetchAuditLogs({ type: AuditLogEvent.BotAdd, limit: 5 })
      .catch(() => null);

    guildOwner = auditLog?.entries.first()?.executor;
  }

  const guildChannel = guild.channels.cache
    .filter(
      (c) => c.type === ChannelType.GuildText && c.permissionsFor(guild.id)?.has('SendMessages'),
    )
    .first() as unknown as TextChannel;

  return { guildOwner, guildChannel };
};

const buildGoalEmbed = (guildName: string, iconURL: string | null, color: ColorResolvable) =>
  new EmbedBuilder().setAuthor({ name: guildName, iconURL: iconURL ?? undefined }).setColor(color);

const buildLogsEmbed = (
  guild: Guild,
  guildOwnerName: string,
  { title, color }: { title: string; color: ColorResolvable },
) =>
  new EmbedBuilder().setColor(color).setThumbnail(guild.iconURL()).setTitle(title)
    .setDescription(stripIndents`
    - Name: ${guild.name}
    - ID: ${guild.id}
    - Owner: ${guild.ownerId} (${guildOwnerName})
    - Member Count: ${guild.memberCount}
  `);

/**
 * @param channelId must be a channel ID in the support server
 */
export const logGuildJoin = async (guild: Guild, channelId: string) => {
  const guildOwner = await guild.client.users.fetch(guild.ownerId);

  await guild.client.cluster.broadcastEval(
    async (client, ctx) => {
      const goalChannel = client.channels.cache.get(ctx.goalChannel);
      const inviteLogChannel = client.channels.cache.get(ctx.inviteLogs);

      if (!goalChannel?.isSendable() || !inviteLogChannel?.isSendable()) {
        return;
      }

      const count = (await client.cluster.fetchClientValues('guilds.cache.size')) as number[];
      const guildCount = count.reduce((p, n) => p + n, 0);

      await inviteLogChannel.send({ embeds: [ctx.logsEmbed] });

      // send message to support server notifying of new guild
      await goalChannel.send({
        content: `${ctx.flushedEmoji} ${ctx.guildName} added me, I'm now in **${guildCount}** servers! 🎉`,
        embeds: [ctx.goalEmbed],
      });
    },
    {
      context: {
        guildName: guild.name,
        goalChannel: channelId,
        inviteLogs: Constants.Channels.inviteLogs,
        flushedEmoji: mascotEmojis.flushed,
        goalEmbed: buildGoalEmbed(
          guild.name,
          guild.iconURL(),
          Constants.Colors.interchatBlue,
        ).toJSON(),
        logsEmbed: buildLogsEmbed(guild, guildOwner.username, {
          color: Constants.Colors.interchatBlue,
          title: '✨ Invited to New Server',
        }).toJSON(),
      },
    },
  );
};

export const logGuildLeave = async (guild: Guild, channelId: string) => {
  const count = (await guild.client.cluster.fetchClientValues('guilds.cache.size')) as number[];
  const guildOwner = await guild.client.users.fetch(guild.ownerId);

  // send message to support server notifying of leave
  // we cant access any variables/functions or anything inside the broadcastEval callback so we pass it in as context
  await guild.client.cluster.broadcastEval(
    async (client, ctx) => {
      const goalChannel = await client.channels.fetch(ctx.goalChannel).catch(() => null);
      const inviteLogChannel = client.channels.cache.get(ctx.inviteLogs);

      if (!goalChannel?.isSendable() || !inviteLogChannel?.isSendable()) {
        return;
      }

      await inviteLogChannel.send({ embeds: [ctx.logsEmbed] });
      await goalChannel.send({
        content: `👢 ${ctx.guildName} kicked me. I'm back to **${ctx.guildCount}** servers ${ctx.cryEmoji}`,
        embeds: [ctx.goalEmbed],
      });
    },
    {
      context: {
        guildName: guild.name,
        goalChannel: channelId,
        inviteLogs: Constants.Channels.inviteLogs,
        guildCount: count.reduce((p, n) => p + n, 0),
        cryEmoji: mascotEmojis.cry,
        goalEmbed: buildGoalEmbed(guild.name, guild.iconURL(), 'Red'),
        logsEmbed: buildLogsEmbed(guild, guildOwner.username, {
          color: Constants.Colors.interchatBlue,
          title: '👢 Kicked from server',
        }).toJSON(),
      },
    },
  );
};