Commit cd3035ec authored by ChanHaeng Lee's avatar ChanHaeng Lee
Browse files

Define functions for fediverse handles

parent c548281b
Loading
Loading
Loading
Loading
+4 −7
Original line number Diff line number Diff line
import { Command } from "@cliffy/command";
import { handleRegexp } from "@fedify/fedify/vocab";
import { convertFediverseHandle } from "@fedify/fedify/vocab";
import { lookupWebFinger } from "@fedify/fedify/webfinger";
import ora from "ora";
import { printJson } from "./utils.ts";
@@ -82,12 +82,9 @@ class InvalidHandleError extends Error {
 * ```
 */
function convertHandleToUrl(handle: string): URL {
  const match = handle.match(handleRegexp);
  if (!match) {
  const url = convertFediverseHandle(handle); // Convert the handle to a URL
  if (!url) {
    throw new InvalidHandleError(handle);
  }

  const [, username, domain] = match;
  // Builds a URL like "https://domain.com/@username"
  return new URL(`https://${domain}/@${username}`);
  return url;
}
+79 −1
Original line number Diff line number Diff line
@@ -52,9 +52,87 @@ export interface LookupObjectOptions {
 * The `user` part can contain alphanumeric characters and some special characters except `@`.
 * The `server` part is all characters after the `@` symbol in the middle.
 */
export const handleRegexp =
const handleRegexp =
  /^@?((?:[-A-Za-z0-9._~!$&'()*+,;=]|%[A-Fa-f0-9]{2})+)@([^@]+)$/;

/**
 * Represents a fediverse handle, which consists of a username and a host.
 * The username can be alphanumeric and may include special characters,
 * while the host is typically a domain name.
 */
export interface FediverseHandle {
  /**
   * The username part of the fediverse handle.
   * It can include alphanumeric characters and some special characters.
   */
  readonly username: string;
  /**
   * The host part of the fediverse handle, typically a domain name.
   * It is the part after the `@` symbol in the handle.
   */
  readonly host: string;
}

/**
 * Parses a fediverse handle in the format `@user@server` or `user@server`.
 * The `user` part can contain alphanumeric characters and some special characters except `@`.
 * The `server` part is all characters after the `@` symbol in the middle.
 *
 * @example
 * ```typescript
 * const handle = parseFediverseHandle("@username@example.com");
 * console.log(handle?.username); // "username"
 * console.log(handle?.host);     // "example.com"
 * ```
 */
export function parseFediverseHandle(
  handle: string,
): FediverseHandle | undefined {
  const match = handleRegexp.exec(handle);
  if (match) {
    return {
      username: match[1],
      host: match[2],
    };
  }
  return undefined;
}

/**
 * Checks if a string is a valid fediverse handle in the format `@user@server` or `user@server`.
 * The `user` part can contain alphanumeric characters and some special characters except `@`.
 * The `server` part is all characters after the `@` symbol in the middle.
 *
 * @example
 * ```typescript
 * console.log(isFediverseHandle("@username@example.com")); // true
 * console.log(isFediverseHandle("username@example.com"));  // true
 * console.log(isFediverseHandle("@username@"));             // false
 * ```
 */
export function isFediverseHandle(
  handle: string,
): handle is `${string}@${string}` {
  return handleRegexp.test(handle);
}

/**
 * Converts a fediverse handle in the format `@user@server` or `user@server`
 * to an `acct:` URI, which is a URL-like identifier for ActivityPub actors.
 *
 * @example
 * ```typescript
 * const identifier = convertFediverseHandle("@username@example.com");
 * console.log(identifier?.href); // "acct:username@example.com"
 * ```
 */
export function convertFediverseHandle(handle: string): URL | undefined {
  const parsed = parseFediverseHandle(handle);
  if (!parsed) return undefined;
  const identifier = new URL(`acct:${parsed.username}@${parsed.host}`);
  return identifier;
}

/**
 * Looks up an ActivityStreams object by its URI (including `acct:` URIs)
 * or a fediverse handle (e.g., `@user@server` or `user@server`).