From 8a45da7363956f02b66f32d5bc3bfe1509bd5ae0 Mon Sep 17 00:00:00 2001 From: Hong Minhee Date: Sun, 12 Jan 2025 15:17:24 +0900 Subject: [PATCH] `mentions()` predicate function --- docs/concepts/text.md | 29 +++++++++++++++++++++++++++++ src/text.test.ts | 17 +++++++++++++++++ src/text.ts | 23 +++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/docs/concepts/text.md b/docs/concepts/text.md index 980114b..02f0e61 100644 --- a/docs/concepts/text.md +++ b/docs/concepts/text.md @@ -452,3 +452,32 @@ The above code will create a text like this: > [!NOTE] > The `markdown()` function does not support raw HTML syntax. + + +Determining if the text mentions an account +------------------------------------------- + +You can determine if the text mentions an account by using the `mentions()` +function. It returns `true` if the text mentions the account, +otherwise `false`: + +~~~~ typescript +import { type Actor, markdown, mention, mentions, text } from "@fedify/botkit"; + +const actor: Actor = getActor( // A hypothetical function that returns an Actor object + "@fedify@hollo.social" +); +const actor2: Actor = getActor("@another@example.com"); + +const md = markdown("Hello, @fedify@hollo.social!"); +console.log(await mentions(md, actor)); // true +console.log(await mentions(md, actor2)); // false + +const txt = text`Hi, ${actor2}!` +console.log(await mentions(txt, actor)); // false +console.log(await mentions(txt, actor2)); // true + +const noMention = text`Hello, world!`; +console.log(await mentions(noMention, actor)); // false +console.log(await mentions(noMention, actor2)); // false +~~~~ diff --git a/src/text.test.ts b/src/text.test.ts index 199c51d..ad1eb6e 100644 --- a/src/text.test.ts +++ b/src/text.test.ts @@ -37,6 +37,7 @@ import { link, markdown, mention, + mentions, type Text, text, } from "./text.ts"; @@ -134,6 +135,22 @@ Deno.test("isText()", () => { assertFalse(isText("Hello, World")); }); +Deno.test("mentions()", async () => { + const session = bot.getSession("https://example.com"); + const actor = new URL("https://hollo.social/@fedify"); + const actor2 = new URL("https://example.com/users/john"); + const actor3 = new Person({ id: actor }); + const t: Text<"block", void> = text`Hello, world!`; + assertFalse(await mentions(session, t, actor)); + assertFalse(await mentions(session, t, actor2)); + assertFalse(await mentions(session, t, actor3)); + + const m: Text<"inline", void> = mention(actor); + assert(await mentions(session, m, actor)); + assertFalse(await mentions(session, m, actor2)); + assert(await mentions(session, m, actor3)); +}); + Deno.test({ name: "text`...`", permissions: {}, diff --git a/src/text.ts b/src/text.ts index 32a8a7f..a90c66e 100644 --- a/src/text.ts +++ b/src/text.ts @@ -79,6 +79,29 @@ export function isText( (value.type === "block" || value.type === "inline"); } +/** + * Checks if a given `actor` is mentioned in a `text`. + * @param session The bot session. + * @param text The text object to check. + * @param actor The actor to check. It can be either an `Actor` object or + * an actor URI. + * @returns `true` if the actor is mentioned in the text, `false` otherwise. + */ +export async function mentions( + session: Session, + text: Text<"block" | "inline", TContextData>, + actor: Actor | URL, +): Promise { + if (isActor(actor)) { + if (actor.id == null) return false; + actor = actor.id; + } + for await (const tag of text.getTags(session)) { + if (tag instanceof Mention && tag.href?.href === actor.href) return true; + } + return false; +} + /** * A text tree that renders a template string with values. You normally * don't need to instantiate this directly; use the {@link text} function