Repository: th-ch/youtube-music
File: src/plugins/discord/utils.ts (TypeScript)

Code before:
 * @returns The truncated string.
 */
export const truncateString = (str: string, length: number): string => {
  if (str.length > length) {
    return `${str.substring(0, length - 3)}...`;
  }
  return str;
};

/**
 * Builds the array of buttons for the Discord Rich Presence activity.
 * @param config - The plugin configuration.
 * @param songInfo - The current song information.
 * @returns An array of buttons or undefined if no buttons are configured.
 */
export const buildDiscordButtons = (
  config: DiscordPluginConfig,
  songInfo: SongInfo,
): GatewayActivityButton[] | undefined => {
  const buttons: GatewayActivityButton[] = [];
  if (config.playOnPearDesktop && songInfo.url) {
    buttons.push({
      label: 'Play on Pear Desktop',
      url: songInfo.url,
    });
  }
  if (!config.hideGitHubButton) {
    buttons.push({
      label: 'View App On GitHub',
      url: 'https://github.com/pear-devs/pear-desktop',
    });
  }
  return buttons.length ? buttons : undefined;
};

/**
 * Pads Hangul fields (title, artist, album) in SongInfo if they are less than 2 characters long.
 * Discord requires fields to be at least 2 characters.
 * @param songInfo - The song information object (will be mutated).
 */
export const padHangulFields = (songInfo: SongInfo): void => {
  (['title', 'artist', 'album'] as const).forEach((key) => {
    const value = songInfo[key];
    if (typeof value === 'string' && value.length > 0 && value.length < 2) {
      songInfo[key] = value + HANGUL_FILLER.repeat(2 - value.length);
    }
  });
};

/**
 * Checks if the difference between two time values indicates a seek operation.

Review comment (suggestion):
Hmm, `.padEnd(2, ' ')` implies that a string of `'  '` is allowed, but that's far from the case.
If the string is empty, it will return the fallback. It's improbable for `.padEnd(2, ' ')` to add 2 characters, it will always add 1.

Maybe it makes sense for this to be?
```suggestion
  if (safeString.length < 2) {
    safeString += '⠀';
  }
```

I am not making a demand, just talking

Code after:
 * @returns The truncated string.
 */
export const truncateString = (str: string, length: number): string => {
  if (str.length > length) {
    return `${str.substring(0, length - 3)}...`;
  }
  return str;
};

/**
 * Sanitizes a string for Discord Rich Presence activity, ensuring it meets length requirements.
 * @param input - The string to sanitize.
 * @param fallback - A fallback string to use if the input is empty or whitespace. Defaults to 'undefined'.
 * @returns The sanitized string, compliant with Discord's requirements.
 */
export function sanitizeActivityText(input: string | undefined, fallback: string = 'undefined'): string {
  const text = (input && input.trim()) ? input.trim() : fallback.trim();
  let safeString = truncateString(text, 128);

  if (safeString.length === 0) {
    return fallback;
  }

  if (safeString.length < 2) {
    safeString = safeString.padEnd(2, '⠀'); // change if necessary
  }

  return safeString;
}

/**
 * Builds the array of buttons for the Discord Rich Presence activity.
 * @param config - The plugin configuration.
 * @param songInfo - The current song information.
 * @returns An array of buttons or undefined if no buttons are configured.
 */
export const buildDiscordButtons = (
  config: DiscordPluginConfig,
  songInfo: SongInfo,
): GatewayActivityButton[] | undefined => {
  const buttons: GatewayActivityButton[] = [];
  if (config.playOnPearDesktop && songInfo.url) {
    buttons.push({
      label: 'Play on Pear Desktop',
      url: songInfo.url,
    });
  }
  if (!config.hideGitHubButton) {
    buttons.push({
      label: 'View App On GitHub',
      url: 'https://github.com/pear-devs/pear-desktop',