├── CLAUDE.md ├── .cursorrules ├── .windsurfrules ├── README.md ├── .github ├── FUNDING.yaml ├── copilot-instructions.md └── workflows │ └── main.yaml ├── codecov.yaml ├── .gitignore ├── docs ├── .gitignore ├── public │ ├── favicon-32x32.png │ └── favicon-192x192.png ├── start │ ├── academy-message.png │ ├── academy-profile.png │ └── academy-search.png ├── changelog.md ├── .vitepress │ ├── theme │ │ ├── index.ts │ │ └── style.css │ └── config.mts ├── package.json ├── index.md ├── deploy │ ├── docker.md │ ├── deno-deploy.md │ ├── self-hosting.md │ └── store-mq.md ├── examples.md ├── intro.md ├── recipes.md ├── concepts │ ├── session.md │ └── repository.md └── start.md ├── mise.toml ├── .vscode ├── extensions.json └── settings.json ├── examples ├── greet │ ├── deno.json │ └── greet.ts └── otp │ ├── deno.json │ └── otp.tsx ├── pnpm-workspace.yaml ├── packages ├── botkit-sqlite │ ├── tsdown.config.ts │ ├── deno.json │ ├── package.json │ └── README.md └── botkit │ ├── tsdown.config.ts │ ├── deno.json │ ├── src │ ├── follow.ts │ ├── components │ │ ├── FollowButton.tsx │ │ ├── Layout.tsx │ │ ├── Follower.tsx │ │ ├── Message.test.tsx │ │ └── Message.tsx │ ├── poll.ts │ ├── mod.ts │ ├── follow-impl.ts │ ├── reaction.ts │ ├── emoji.ts │ ├── bot.test.ts │ ├── emoji.test.ts │ ├── follow-impl.test.ts │ ├── events.ts │ ├── session.ts │ └── message.ts │ ├── README.md │ └── package.json ├── SECURITY.md ├── deno.json ├── scripts └── check_versions.ts ├── AGENTS.md └── CHANGES.md /CLAUDE.md: -------------------------------------------------------------------------------- 1 | AGENTS.md -------------------------------------------------------------------------------- /.cursorrules: -------------------------------------------------------------------------------- 1 | AGENTS.md -------------------------------------------------------------------------------- /.windsurfrules: -------------------------------------------------------------------------------- 1 | AGENTS.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | packages/botkit/README.md -------------------------------------------------------------------------------- /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | github: dahlia 2 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | ../AGENTS.md -------------------------------------------------------------------------------- /codecov.yaml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "**/*.tsx" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .claude/ 2 | coverage/ 3 | dist/ 4 | node_modules/ 5 | fedify-botkit-*.tgz 6 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .jsr-cache.json 2 | .vitepress/cache/ 3 | .vitepress/dist/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | bun = "latest" 3 | deno = "2" 4 | node = "22" 5 | "npm:pnpm" = "latest" 6 | -------------------------------------------------------------------------------- /docs/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/botkit/HEAD/docs/public/favicon-32x32.png -------------------------------------------------------------------------------- /docs/start/academy-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/botkit/HEAD/docs/start/academy-message.png -------------------------------------------------------------------------------- /docs/start/academy-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/botkit/HEAD/docs/start/academy-profile.png -------------------------------------------------------------------------------- /docs/start/academy-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/botkit/HEAD/docs/start/academy-search.png -------------------------------------------------------------------------------- /docs/public/favicon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/botkit/HEAD/docs/public/favicon-192x192.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "denoland.vscode-deno", 4 | "streetsidesoftware.code-spell-checker" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /examples/greet/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno serve --allow-net --allow-env --watch greet.ts", 4 | "prod": "deno serve --allow-net --allow-env greet.ts" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/otp/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno serve --allow-net --allow-env --watch otp.tsx", 4 | "prod": "deno serve --allow-net --allow-env otp.tsx" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | The changelog contains a list of changes made to BotKit. It is useful for 4 | developers who want to know what has changed in the library. 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | - docs 4 | 5 | catalog: 6 | "@fedify/fedify": 1.9.0-dev.1516+8f42bff1 7 | "@js-temporal/polyfill": ^0.5.1 8 | "@logtape/logtape": ^1.0.4 9 | hono: ^4.8.2 10 | tsdown: ^0.12.8 11 | x-forwarded-fetch: ^0.2.0 12 | -------------------------------------------------------------------------------- /packages/botkit-sqlite/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | entry: "src/mod.ts", 5 | dts: { 6 | sourcemap: true, 7 | }, 8 | format: "esm", 9 | platform: "node", 10 | outputOptions: { 11 | intro: ` 12 | import { Temporal, toTemporalInstant } from "@js-temporal/polyfill"; 13 | Date.prototype.toTemporalInstant = toTemporalInstant; 14 | `, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/botkit/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | "src/**/*.ts", 6 | "src/**/*.tsx", 7 | ], 8 | dts: { 9 | sourcemap: true, 10 | }, 11 | format: "esm", 12 | platform: "node", 13 | unbundle: true, 14 | outputOptions: { 15 | intro: ` 16 | import { Temporal, toTemporalInstant } from "@js-temporal/polyfill"; 17 | Date.prototype.toTemporalInstant = toTemporalInstant; 18 | `, 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Security policy 4 | =============== 5 | 6 | Supported versions 7 | ------------------ 8 | 9 | We support the latest two minor versions of the library. For example, if the 10 | latest version is 0.12.0, we support 0.12.x and 0.11.x. 11 | 12 | 13 | Reporting a vulnerability 14 | ------------------------- 15 | 16 | If you think you have found a security issue, please *do not* open a public 17 | issue. Instead, please open a private vulnerability report by [creating a new 18 | draft security advisory][1]. 19 | 20 | We will review your report and respond within 48 hours. 21 | 22 | [1]: https://github.com/fedify-dev/botkit/security/advisories/new 23 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import TwoslashFloatingVue from "@shikijs/vitepress-twoslash/client"; 3 | import "@shikijs/vitepress-twoslash/style.css"; 4 | import "virtual:group-icons.css"; 5 | import type { EnhanceAppContext, Theme } from "vitepress"; 6 | import DefaultTheme from "vitepress/theme"; 7 | import { h } from "vue"; 8 | import "./style.css"; 9 | 10 | export default { 11 | extends: DefaultTheme, 12 | Layout: () => { 13 | return h(DefaultTheme.Layout, null, { 14 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 15 | }); 16 | }, 17 | enhanceApp({ app, router, siteData }: EnhanceAppContext) { 18 | app.use(TwoslashFloatingVue); 19 | }, 20 | } satisfies Theme; 21 | -------------------------------------------------------------------------------- /packages/botkit-sqlite/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fedify/botkit-sqlite", 3 | "version": "0.4.0", 4 | "license": "AGPL-3.0-only", 5 | "exports": { 6 | ".": "./src/mod.ts" 7 | }, 8 | "exclude": [ 9 | "dist", 10 | "junit.xml", 11 | "package.json" 12 | ], 13 | "fmt": { 14 | "exclude": [ 15 | "*.md", 16 | "*.yaml", 17 | "*.yml" 18 | ] 19 | }, 20 | "tasks": { 21 | "test": "deno test --allow-write --allow-read --allow-env --parallel", 22 | "test:node": "pnpm install && pnpm test", 23 | "test-all": { 24 | "dependencies": [ 25 | "check", 26 | "test", 27 | "test:node" 28 | ] 29 | }, 30 | "coverage": "deno task test --coverage --clean && deno coverage --html" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@fedify/botkit": "workspace:", 4 | "@fedify/botkit-sqlite": "workspace:", 5 | "@fedify/fedify": "catalog:", 6 | "@fedify/postgres": "^1.8.7", 7 | "@fedify/redis": "^1.8.7", 8 | "@fedify/sqlite": "^1.8.7", 9 | "@js-temporal/polyfill": "catalog:", 10 | "@shikijs/vitepress-twoslash": "^3.20.0", 11 | "@types/deno": "^2.3.0", 12 | "@types/node": "^24.0.3", 13 | "ioredis": "^5.6.1", 14 | "markdown-it-deflist": "^3.0.0", 15 | "markdown-it-footnote": "^4.0.0", 16 | "markdown-it-jsr-ref": "^0.4.1", 17 | "postgres": "^3.4.7", 18 | "srvx": "^0.8.0", 19 | "typescript": "^5.8.3", 20 | "vitepress": "^2.0.0-alpha.15", 21 | "vitepress-plugin-group-icons": "^1.3.3", 22 | "vitepress-plugin-llms": "^1.1.0" 23 | }, 24 | "scripts": { 25 | "dev": "cd ../ && pnpm run -r build && cd docs/ && vitepress dev", 26 | "build": "cd ../ && pnpm run -r build && cd docs/ && vitepress build", 27 | "preview": "cd ../ && pnpm run -r build && cd docs/ && vitepress preview" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: BotKit by Fedify 7 | text: Simple ActivityPub bot framework 8 | tagline: A framework for creating your fediverse bots 9 | image: 10 | src: /logo.svg 11 | alt: BotKit by Fedify 12 | actions: 13 | - theme: brand 14 | text: Getting started 15 | link: /start.md 16 | - theme: alt 17 | text: What is BotKit? 18 | link: /intro.md 19 | - theme: alt 20 | text: Examples 21 | link: /examples.md 22 | 23 | features: 24 | - title: Standalone 25 | icon: 🔋 26 | details: >- 27 | Using BotKit, you can create standalone ActivityPub bots rather than 28 | Mastodon/Misskey bots. Hence, you are free from the constraints of 29 | the existing platforms. 30 | - title: Easy to use 31 | icon: 🧩 32 | details: >- 33 | BotKit is designed to be easy to use. You can create your bot with 34 | just a few lines of code. It's also fully written in TypeScript, 35 | so you can enjoy the type safety. 36 | - title: Easy to deploy 37 | icon: 🚀 38 | details: >- 39 | BotKit is designed to be easy to deploy, with minimal dependencies. 40 | You can deploy your bot on Deno Deploy, Fly.io, Railway, or any other 41 | virtual servers. 42 | - title: Powered by Fedify 43 | icon: 44 | src: /fedify.svg 45 | alt: Fedify 46 | width: 36 47 | height: 36 48 | details: >- 49 | BotKit is powered by Fedify, a lower-level rock-solid ActivityPub framework. 50 | No worries about the underlying protocol or the compatibility with other 51 | ActivityPub implementations. 52 | --- 53 | -------------------------------------------------------------------------------- /packages/botkit-sqlite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fedify/botkit-sqlite", 3 | "version": "0.4.0", 4 | "description": "SQLite-based repository for BotKit", 5 | "license": "AGPL-3.0-only", 6 | "author": { 7 | "name": "Hong Minhee", 8 | "email": "hong@minhee.org", 9 | "url": "https://hongminhee.org/" 10 | }, 11 | "homepage": "https://botkit.fedify.dev/", 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/fedify-dev/botkit.git", 15 | "directory": "packages/botkit-sqlite" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/fedify-dev/botkit/issues" 19 | }, 20 | "funding": [ 21 | "https://opencollective.com/fedify", 22 | "https://github.com/sponsors/dahlia" 23 | ], 24 | "engines": { 25 | "deno": ">=2.0.0", 26 | "node": ">=22.0.0" 27 | }, 28 | "type": "module", 29 | "module": "./dist/mod.js", 30 | "types": "./dist/mod.d.ts", 31 | "exports": { 32 | ".": { 33 | "types": "./dist/mod.d.ts", 34 | "import": "./dist/mod.js" 35 | }, 36 | "./package.json": "./package.json" 37 | }, 38 | "sideEffects": false, 39 | "files": [ 40 | "dist", 41 | "LICENSE", 42 | "package.json", 43 | "README.md" 44 | ], 45 | "peerDependencies": { 46 | "@fedify/botkit": "workspace:" 47 | }, 48 | "dependencies": { 49 | "@fedify/fedify": "catalog:", 50 | "@js-temporal/polyfill": "catalog:", 51 | "@logtape/logtape": "catalog:" 52 | }, 53 | "devDependencies": { 54 | "tsdown": "catalog:" 55 | }, 56 | "scripts": { 57 | "build": "tsdown", 58 | "prepack": "tsdown", 59 | "prepublish": "deno task check && tsdown", 60 | "test": "tsdown && cd src/ && node --test --experimental-transform-types" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/botkit/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fedify/botkit", 3 | "version": "0.4.0", 4 | "license": "AGPL-3.0-only", 5 | "exports": { 6 | ".": "./src/mod.ts", 7 | "./bot": "./src/bot.ts", 8 | "./emoji": "./src/emoji.ts", 9 | "./events": "./src/events.ts", 10 | "./follow": "./src/follow.ts", 11 | "./message": "./src/message.ts", 12 | "./poll": "./src/poll.ts", 13 | "./reaction": "./src/reaction.ts", 14 | "./repository": "./src/repository.ts", 15 | "./session": "./src/session.ts", 16 | "./text": "./src/text.ts" 17 | }, 18 | "imports": { 19 | "@fedify/markdown-it-hashtag": "jsr:@fedify/markdown-it-hashtag@^0.3.0", 20 | "@fedify/markdown-it-mention": "jsr:@fedify/markdown-it-mention@^0.3.0", 21 | "@phensley/language-tag": "npm:@phensley/language-tag@^1.12.2", 22 | "html-entities": "npm:html-entities@^2.6.0", 23 | "markdown-it": "npm:markdown-it@^14.1.0", 24 | "mime-db": "npm:mime-db@^1.54.0", 25 | "tsdown": "npm:tsdown@^0.12.8", 26 | "url-template": "npm:url-template@^3.1.1", 27 | "uuid": "npm:uuid@^11.1.0", 28 | "xss": "npm:xss@^1.0.15" 29 | }, 30 | "exclude": [ 31 | "dist", 32 | "junit.xml", 33 | "src/css" 34 | ], 35 | "fmt": { 36 | "exclude": [ 37 | "*.md", 38 | "*.yaml", 39 | "*.yml", 40 | "src/static/*.ts" 41 | ] 42 | }, 43 | "tasks": { 44 | "test": "deno test --allow-env=NODE_V8_COVERAGE,JEST_WORKER_ID --allow-net=hollo.social --parallel", 45 | "test:node": "pnpm install && pnpm test", 46 | "test-all": { 47 | "dependencies": [ 48 | "check", 49 | "test", 50 | "test:node" 51 | ] 52 | }, 53 | "coverage": "deno task test --coverage --clean && deno coverage --html" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "workspace": [ 3 | "packages/*", 4 | "examples/*" 5 | ], 6 | "unstable": [ 7 | "kv", 8 | "temporal" 9 | ], 10 | "imports": { 11 | "@fedify/fedify": "jsr:@fedify/fedify@1.9.0-dev.1516+8f42bff1", 12 | "@logtape/logtape": "jsr:@logtape/logtape@^1.0.4", 13 | "@std/fs": "jsr:@std/fs@^1.0.19", 14 | "@std/path": "jsr:@std/path@^1.1.1", 15 | "hono": "jsr:@hono/hono@^4.8.2", 16 | "tsdown": "npm:tsdown@^0.12.8", 17 | "x-forwarded-fetch": "jsr:@hongminhee/x-forwarded-fetch@^0.2.0" 18 | }, 19 | "nodeModulesDir": "none", 20 | "exclude": [ 21 | ".github", 22 | "docs", 23 | "dist", 24 | "junit.xml", 25 | "logo.svg", 26 | "package.json", 27 | "src/css" 28 | ], 29 | "fmt": { 30 | "exclude": [ 31 | "*.md", 32 | "*.yaml", 33 | "*.yml" 34 | ] 35 | }, 36 | "tasks": { 37 | "check": { 38 | "dependencies": [ 39 | "check-versions" 40 | ], 41 | "command": "deno check && deno lint && deno fmt --check && deno publish --dry-run --allow-dirty" 42 | }, 43 | "check-versions": "deno run --allow-read --allow-write scripts/check_versions.ts", 44 | "install": "deno cache packages/*/src/*.ts", 45 | "test": "deno test --allow-read --allow-write --allow-env --allow-net=hollo.social --parallel", 46 | "test:node": "pnpm install && pnpm run -r test", 47 | "test-all": { 48 | "dependencies": [ 49 | "check", 50 | "test", 51 | "test:node" 52 | ] 53 | }, 54 | "coverage": "deno task test --coverage --clean && deno coverage --html", 55 | "hooks:install": "deno run --allow-read=deno.json,.git/hooks/ --allow-write=.git/hooks/ jsr:@hongminhee/deno-task-hooks", 56 | "hooks:pre-commit": "deno task check" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/deploy/docker.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Learn how to deploy your BotKit bot using Docker containers on platforms like 4 | Fly.io and Railway. 5 | --- 6 | 7 | Docker 8 | ====== 9 | 10 | Docker containers provide a consistent deployment environment and can be hosted 11 | on various platforms like [Fly.io], [Railway], or any container hosting service. 12 | 13 | [Fly.io]: https://fly.io/ 14 | [Railway]: https://railway.com/ 15 | 16 | 17 | Creating a *Dockerfile* 18 | ----------------------- 19 | 20 | Create a *Dockerfile* in your project root: 21 | 22 | ~~~~ dockerfile [Dockerfile] 23 | FROM denoland/deno:2.1.9 24 | 25 | WORKDIR /app 26 | 27 | # Cache dependencies 28 | COPY deno.json deno.json 29 | COPY deno.lock deno.lock 30 | RUN deno install 31 | 32 | # Copy source code 33 | COPY . . 34 | 35 | # The bot needs network access and environment variables 36 | ENV SERVER_NAME=your-domain.com 37 | 38 | # Run the bot 39 | CMD ["deno", "run", "-A", "bot.ts"] 40 | ~~~~ 41 | 42 | 43 | Deploying to [Fly.io] 44 | --------------------- 45 | 46 | 1. Install the [Fly.io CLI] 47 | 48 | 2. Initialize your Fly.io app: 49 | 50 | ~~~~ bash 51 | fly launch 52 | ~~~~ 53 | 54 | 3. Configure environment variables: 55 | 56 | ~~~~ bash 57 | fly secrets set SERVER_NAME=your-domain.com 58 | ~~~~ 59 | 60 | 4. Deploy your app: 61 | 62 | ~~~~ bash 63 | fly deploy 64 | ~~~~ 65 | 66 | [Fly.io CLI]: https://fly.io/docs/flyctl/ 67 | 68 | 69 | Deploying to [Railway] 70 | ---------------------- 71 | 72 | 1. Create a new project on [Railway] 73 | 74 | 2. Connect your GitHub repository 75 | 76 | 3. Configure environment variables in the Railway dashboard 77 | 78 | 4. Railway will automatically build and deploy your container 79 | 80 | 81 | -------------------------------------------------------------------------------- /packages/botkit/src/follow.ts: -------------------------------------------------------------------------------- 1 | // BotKit by Fedify: A framework for creating ActivityPub bots 2 | // Copyright (C) 2025 Hong Minhee 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as 6 | // published by the Free Software Foundation, either version 3 of the 7 | // License, or (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | import type { Actor, Follow } from "@fedify/fedify/vocab"; 17 | 18 | /** 19 | * A follow request to the bot. 20 | */ 21 | export interface FollowRequest { 22 | /** 23 | * The URI of the follow request. 24 | */ 25 | readonly id: URL; 26 | 27 | /** 28 | * The raw follow request object. 29 | */ 30 | readonly raw: Follow; 31 | 32 | /** 33 | * The follower actor. 34 | */ 35 | readonly follower: Actor; 36 | 37 | /** 38 | * The state of the follow request. 39 | * 40 | * - `"pending"`: The follow request is pending. 41 | * - `"accepted"`: The follow request is accepted. 42 | * - `"rejected"`: The follow request is rejected. 43 | */ 44 | readonly state: "pending" | "accepted" | "rejected"; 45 | 46 | /** 47 | * Accepts the follow request. 48 | * @throws {TypeError} The follow request is not pending. 49 | */ 50 | accept(): Promise; 51 | 52 | /** 53 | * Rejects the follow request. 54 | * @throws {TypeError} The follow request is not pending. 55 | */ 56 | reject(): Promise; 57 | } 58 | -------------------------------------------------------------------------------- /packages/botkit/src/components/FollowButton.tsx: -------------------------------------------------------------------------------- 1 | /** @jsxImportSource hono/jsx */ 2 | import type { BotImpl } from "../bot-impl.ts"; 3 | 4 | export interface FollowButtonProps { 5 | readonly bot: BotImpl; 6 | } 7 | 8 | export function FollowButton({ bot }: FollowButtonProps) { 9 | return ( 10 | <> 11 | 19 | 20 |
21 |
22 |

Follow {bot.name ?? bot.username}

23 |
30 |
31 |

Enter your fediverse handle to follow this account:

32 |
33 | 41 | 44 |
45 |
46 |
47 |
48 |