@fedify/botkit 0.3.0-dev.108

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +75 -0
  3. package/dist/bot-impl.d.ts +111 -0
  4. package/dist/bot-impl.d.ts.map +1 -0
  5. package/dist/bot-impl.js +602 -0
  6. package/dist/bot-impl.js.map +1 -0
  7. package/dist/bot-impl.test.d.ts +2 -0
  8. package/dist/bot-impl.test.js +1642 -0
  9. package/dist/bot-impl.test.js.map +1 -0
  10. package/dist/bot.d.ts +270 -0
  11. package/dist/bot.d.ts.map +1 -0
  12. package/dist/bot.js +118 -0
  13. package/dist/bot.js.map +1 -0
  14. package/dist/bot.test.d.ts +2 -0
  15. package/dist/bot.test.js +80 -0
  16. package/dist/bot.test.js.map +1 -0
  17. package/dist/components/Layout.d.ts +26 -0
  18. package/dist/components/Layout.d.ts.map +1 -0
  19. package/dist/components/Layout.js +36 -0
  20. package/dist/components/Layout.js.map +1 -0
  21. package/dist/components/Message.d.ts +21 -0
  22. package/dist/components/Message.d.ts.map +1 -0
  23. package/dist/components/Message.js +99 -0
  24. package/dist/components/Message.js.map +1 -0
  25. package/dist/components/Message.test.d.ts +2 -0
  26. package/dist/components/Message.test.js +32 -0
  27. package/dist/components/Message.test.js.map +1 -0
  28. package/dist/deno.js +73 -0
  29. package/dist/deno.js.map +1 -0
  30. package/dist/emoji.d.ts +87 -0
  31. package/dist/emoji.d.ts.map +1 -0
  32. package/dist/emoji.js +37 -0
  33. package/dist/emoji.js.map +1 -0
  34. package/dist/emoji.test.d.ts +2 -0
  35. package/dist/emoji.test.js +109 -0
  36. package/dist/emoji.test.js.map +1 -0
  37. package/dist/events.d.ts +110 -0
  38. package/dist/events.d.ts.map +1 -0
  39. package/dist/events.js +4 -0
  40. package/dist/follow-impl.d.ts +23 -0
  41. package/dist/follow-impl.d.ts.map +1 -0
  42. package/dist/follow-impl.js +51 -0
  43. package/dist/follow-impl.js.map +1 -0
  44. package/dist/follow-impl.test.d.ts +2 -0
  45. package/dist/follow-impl.test.js +110 -0
  46. package/dist/follow-impl.test.js.map +1 -0
  47. package/dist/follow.d.ts +44 -0
  48. package/dist/follow.d.ts.map +1 -0
  49. package/dist/follow.js +4 -0
  50. package/dist/message-impl.d.ts +54 -0
  51. package/dist/message-impl.d.ts.map +1 -0
  52. package/dist/message-impl.js +439 -0
  53. package/dist/message-impl.js.map +1 -0
  54. package/dist/message-impl.test.d.ts +2 -0
  55. package/dist/message-impl.test.js +519 -0
  56. package/dist/message-impl.test.js.map +1 -0
  57. package/dist/message.d.ts +223 -0
  58. package/dist/message.d.ts.map +1 -0
  59. package/dist/message.js +8 -0
  60. package/dist/mod.d.ts +13 -0
  61. package/dist/mod.js +13 -0
  62. package/dist/pages.d.ts +20 -0
  63. package/dist/pages.d.ts.map +1 -0
  64. package/dist/pages.js +361 -0
  65. package/dist/pages.js.map +1 -0
  66. package/dist/reaction.d.ts +90 -0
  67. package/dist/reaction.d.ts.map +1 -0
  68. package/dist/reaction.js +7 -0
  69. package/dist/repository.d.ts +323 -0
  70. package/dist/repository.d.ts.map +1 -0
  71. package/dist/repository.js +483 -0
  72. package/dist/repository.js.map +1 -0
  73. package/dist/repository.test.d.ts +2 -0
  74. package/dist/repository.test.js +336 -0
  75. package/dist/repository.test.js.map +1 -0
  76. package/dist/session-impl.d.ts +32 -0
  77. package/dist/session-impl.d.ts.map +1 -0
  78. package/dist/session-impl.js +195 -0
  79. package/dist/session-impl.js.map +1 -0
  80. package/dist/session-impl.test.d.ts +20 -0
  81. package/dist/session-impl.test.d.ts.map +1 -0
  82. package/dist/session-impl.test.js +464 -0
  83. package/dist/session-impl.test.js.map +1 -0
  84. package/dist/session.d.ts +139 -0
  85. package/dist/session.d.ts.map +1 -0
  86. package/dist/session.js +4 -0
  87. package/dist/text.d.ts +391 -0
  88. package/dist/text.d.ts.map +1 -0
  89. package/dist/text.js +640 -0
  90. package/dist/text.js.map +1 -0
  91. package/dist/text.test.d.ts +2 -0
  92. package/dist/text.test.js +473 -0
  93. package/dist/text.test.js.map +1 -0
  94. package/package.json +137 -0
@@ -0,0 +1,223 @@
1
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
2
+ Date.prototype.toTemporalInstant = toTemporalInstant;
3
+ import { AuthorizedLike, AuthorizedReaction } from "./reaction.js";
4
+ import { Text } from "./text.js";
5
+ import { SessionPublishOptions, SessionPublishOptionsWithClass } from "./session.js";
6
+ import { DeferredCustomEmoji, Emoji } from "./emoji.js";
7
+ import { Actor, Actor as Actor$1, Announce, Article, Article as Article$1, Audio, ChatMessage, ChatMessage as ChatMessage$1, Document, Document as Document$1, Emoji as Emoji$1, Hashtag, Hashtag as Hashtag$1, Image, Note, Note as Note$1, Question, Question as Question$1, Video, isActor } from "@fedify/fedify/vocab";
8
+ import { LanguageTag, LanguageTag as LanguageTag$1, parseLanguageTag } from "@phensley/language-tag";
9
+
10
+ //#region src/message.d.ts
11
+ /**
12
+ * A possible message class.
13
+ */
14
+ type MessageClass = Article$1 | ChatMessage$1 | Note$1 | Question$1;
15
+ /**
16
+ * The visibility of a message.
17
+ */
18
+ type MessageVisibility =
19
+ /**
20
+ * Signifies that the message is public; it can be seen and discovered by
21
+ * anyone.
22
+ */
23
+ "public"
24
+ /**
25
+ * Signifies that the message is quietly public; it can be seen by anyone,
26
+ * but it will not appear in public timelines or search results.
27
+ */ | "unlisted"
28
+ /**
29
+ * Signifies that the message is only visible to followers of the account.
30
+ * It will not appear in public timelines or search results.
31
+ */ | "followers"
32
+ /**
33
+ * Signifies that the message is private; it can only be seen by mentioned
34
+ * accounts.
35
+ */ | "direct"
36
+ /**
37
+ * Signifies that the message is unknown; it is not clear who can see it.
38
+ * It is usually the case when the message is published by a minor fediverse
39
+ * server that is incompatible with Mastodon-style visibility.
40
+ */ | "unknown";
41
+ /**
42
+ * A message in the ActivityPub network. It is a thin wrapper around
43
+ * a Fedify object: an `Article`, a `ChatMessage`, a `Note`, or a `Question`.
44
+ * @typeParam T The class of the message. One of `Article`, `ChatMessage`,
45
+ * `Note`, or `Question`.
46
+ * @typeParam TContextData The type of the context data.
47
+ */
48
+ interface Message<T extends MessageClass, TContextData> {
49
+ /**
50
+ * The underlying raw message object.
51
+ */
52
+ readonly raw: T;
53
+ /**
54
+ * The URI of the message.
55
+ */
56
+ readonly id: URL;
57
+ /**
58
+ * The actor who published the message.
59
+ */
60
+ readonly actor: Actor$1;
61
+ /**
62
+ * The visibility of the message.
63
+ */
64
+ readonly visibility: MessageVisibility;
65
+ /**
66
+ * The language of the message if the content is tagged with a language.
67
+ */
68
+ readonly language?: LanguageTag$1;
69
+ /**
70
+ * The plain text content of the message.
71
+ */
72
+ readonly text: string;
73
+ /**
74
+ * The HTML content of the message.
75
+ */
76
+ readonly html: string;
77
+ /**
78
+ * The original message in reply to, if the message is a reply.
79
+ */
80
+ readonly replyTarget?: Message<MessageClass, TContextData>;
81
+ /**
82
+ * The actors mentioned in the message.
83
+ */
84
+ readonly mentions: readonly Actor$1[];
85
+ /**
86
+ * The hashtags used in the message.
87
+ */
88
+ readonly hashtags: readonly Hashtag$1[];
89
+ /**
90
+ * The media attachments of the message.
91
+ */
92
+ readonly attachments: readonly Document$1[];
93
+ /**
94
+ * The message quoted by this message, if any.
95
+ * @since 0.2.0
96
+ */
97
+ readonly quoteTarget?: Message<MessageClass, TContextData>;
98
+ /**
99
+ * The published time of the message.
100
+ */
101
+ readonly published?: Temporal.Instant;
102
+ /**
103
+ * The updated time of the message, if it is updated.
104
+ */
105
+ readonly updated?: Temporal.Instant;
106
+ /**
107
+ * Publishes a reply to the message.
108
+ * @param text The content of the message.
109
+ * @param options The options for publishing the message.
110
+ * @returns The published message.
111
+ */
112
+ reply(text: Text<"block", TContextData>, options?: SessionPublishOptions<TContextData>): Promise<AuthorizedMessage<Note$1, TContextData>>;
113
+ /**
114
+ * Publishes a reply to the message.
115
+ * @typeParam T The class of the published message.
116
+ * @param text The content of the message.
117
+ * @param options The options for publishing the message.
118
+ * @returns The published message.
119
+ */
120
+ reply<T extends MessageClass>(text: Text<"block", TContextData>, options?: SessionPublishOptionsWithClass<T, TContextData>): Promise<AuthorizedMessage<T, TContextData>>;
121
+ /**
122
+ * Shares the message.
123
+ *
124
+ * It throws an error if the visibility of the message is neither `"public"`
125
+ * nor `"unlisted"`.
126
+ * @param options The options for sharing the message.
127
+ * @returns The shared message.
128
+ * @throws {TypeError} If the visibility of the message is not `"public"` or
129
+ * `"unlisted"`.
130
+ */
131
+ share(options?: MessageShareOptions): Promise<AuthorizedSharedMessage<T, TContextData>>;
132
+ /**
133
+ * Likes the message.
134
+ * @returns The like object.
135
+ */
136
+ like(): Promise<AuthorizedLike<TContextData>>;
137
+ /**
138
+ * Reacts to the message with a Unicode emoji or a custom emoji.
139
+ * @param emoji The emoji to react with. It can be either a Unicode emoji or
140
+ * a custom emoji.
141
+ * @returns The reaction object.
142
+ * @since 0.2.0
143
+ */
144
+ react(emoji: Emoji | Emoji$1 | DeferredCustomEmoji<TContextData>): Promise<AuthorizedReaction<TContextData>>;
145
+ }
146
+ /**
147
+ * An authorized message in the ActivityPub network. Usually it is a message
148
+ * published by the bot itself.
149
+ * @typeParam T The class of the message. One of `Article`, `ChatMessage`,
150
+ * `Note`, or `Question`.
151
+ * @typeParam TContextData The type of the context data.
152
+ */
153
+ interface AuthorizedMessage<T extends MessageClass, TContextData> extends Message<T, TContextData> {
154
+ /**
155
+ * Updates the message with new content.
156
+ * @param text The new content of the message.
157
+ */
158
+ update(text: Text<"block", TContextData>): Promise<void>;
159
+ /**
160
+ * Deletes the message, if possible.
161
+ *
162
+ * If the message is already deleted, it will be a no-op.
163
+ */
164
+ delete(): Promise<void>;
165
+ }
166
+ /**
167
+ * Options for sharing a message.
168
+ */
169
+ interface MessageShareOptions {
170
+ /**
171
+ * The visibility of the shared message. If omitted, the visibility of the
172
+ * original message will be used.
173
+ */
174
+ readonly visibility?: Exclude<MessageVisibility, "direct" | "unknown">;
175
+ }
176
+ /**
177
+ * A shared message in the ActivityPub network. It is a thin wrapper around
178
+ * an `Announce`, which is a Fedify object.
179
+ * @typeParam T The class of the message. One of `Article`, `ChatMessage`,
180
+ * `Note`, or `Question`.
181
+ * @typeParam TContextData The type of the context data.
182
+ */
183
+ interface SharedMessage<T extends MessageClass, TContextData> {
184
+ /**
185
+ * The underlying raw shared message object.
186
+ */
187
+ readonly raw: Announce;
188
+ /**
189
+ * The URI of the shared message.
190
+ */
191
+ readonly id: URL;
192
+ /**
193
+ * The actor who shared the message.
194
+ */
195
+ readonly actor: Actor$1;
196
+ /**
197
+ * The visibility of the shared message.
198
+ */
199
+ readonly visibility: MessageVisibility;
200
+ /**
201
+ * The original message.
202
+ */
203
+ readonly original: Message<T, TContextData>;
204
+ }
205
+ /**
206
+ * An authorized shared message in the ActivityPub network. Usually it is a
207
+ * message shared by the bot itself.
208
+ * @typeParam T The class of the message. One of `Article`, `ChatMessage`,
209
+ * `Note`, or `Question`.
210
+ * @typeParam TContextData The type of the context data.
211
+ */
212
+ interface AuthorizedSharedMessage<T extends MessageClass, TContextData> extends SharedMessage<T, TContextData> {
213
+ /**
214
+ * Undoes the shared message.
215
+ *
216
+ * If the shared message is already undone, it silently fails.
217
+ */
218
+ unshare(): Promise<void>;
219
+ }
220
+ //# sourceMappingURL=message.d.ts.map
221
+ //#endregion
222
+ export { Actor, Article, Audio, AuthorizedMessage, AuthorizedSharedMessage, ChatMessage, Document, Hashtag, Image, LanguageTag, Message, MessageClass, MessageShareOptions, MessageVisibility, Note, Question, SharedMessage, Video, isActor, parseLanguageTag };
223
+ //# sourceMappingURL=message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.d.ts","names":[],"sources":["../src/message.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;AAoDY,KAAA,YAAA,GAAe,SAAH,GAAa,aAAb,GAA2B,MAA3B,GAAkC,UAAlC;;;;AAA2B,KAKvC,iBAAA;;AALsD;AAKlE;AAmCA;;;;;MAcuB;;;;MAyBS;;;;MAqB2B;;;;;MAoBX,SAAlC;;;;;;;;AAY+B,UA5F5B,OA4F4B,CAAA,UA5FV,YA4FU,EAAA,YAAA,CAAA,CAAA;EAAC;;;EACd,SAAE,GAAA,EAzFlB,CAyFkB;EAAY;;;EAab,SACI,EAAA,EAlGtB,GAkGsB;EAAC;;;EAA1B,SAMqB,KAAA,EAnGf,OAmGe;EAAY;;;EAU7B,SAAG,UAAA,EAxGI,iBAwGJ;EAAW;;;EACc,SAA/B,QAAA,CAAA,EApGS,aAoGT;EAAkB;AAAnB;AAUZ;EAAkC,SAAA,IAAA,EAAA,MAAA;EAAA;;;EACD,SAKJ,IAAA,EAAA,MAAA;EAAY;;;EAOtB,SAZT,WAAA,CAAA,EAhGe,OAgGf,CAhGuB,YAgGvB,EAhGqC,YAgGrC,CAAA;EAAO;AAkBjB;;EAAoC,SAKJ,QAAA,EAAA,SAlHF,OAkHE,EAAA;EAAiB;AAAlB;AAU/B;EAA8B,SAAA,QAAA,EAAA,SAvHA,SAuHA,EAAA;EAAA;;;EASZ,SAKA,WAAA,EAAA,SAhIe,UAgIf,EAAA;EAAK;;;;EAUK,SAAA,WAAA,CAAA,EApIH,OAoIG,CApIK,YAoIL,EApImB,YAoInB,CAAA;EAUX;;;EAA8C,SACvC,SAAA,CAAA,EA1ID,QAAA,CAAS,OA0IR;EAAC;;;EAAF,SAAA,OAAA,CAAA,EArIF,QAAA,CAAS,OAqIP;;;;;;;cA5Hb,cAAc,yBACV,sBAAsB,gBAC/B,QAAQ,kBAAkB,QAAM;;;;;;;;kBASnB,oBACR,cAAc,yBACV,+BAA+B,GAAG,gBAC3C,QAAQ,kBAAkB,GAAG;;;;;;;;;;;kBAapB,sBACT,QAAQ,wBAAwB,GAAG;;;;;UAM9B,QAAQ,eAAe;;;;;;;;eAUtB,QAAQ,UAAc,oBAAoB,gBAChD,QAAQ,mBAAmB;;;;;;;;;UAUf,4BAA4B,oCACnC,QAAQ,GAAG;;;;;eAKN,cAAc,gBAAgB;;;;;;YAOjC;;;;;UAMK,mBAAA;;;;;wBAKO,QAAQ;;;;;;;;;UAUf,wBAAwB;;;;gBAIzB;;;;eAKD;;;;kBAKG;;;;uBAKK;;;;qBAKF,QAAQ,GAAG;;;;;;;;;UAUf,kCAAkC,oCACzC,cAAc,GAAG;;;;;;aAMd"}
@@ -0,0 +1,8 @@
1
+
2
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
3
+ Date.prototype.toTemporalInstant = toTemporalInstant;
4
+
5
+ import { Article, Audio, ChatMessage, Document, Hashtag, Image, Note, Question, Video, isActor } from "@fedify/fedify/vocab";
6
+ import { LanguageTag, parseLanguageTag } from "@phensley/language-tag";
7
+
8
+ export { Article, Audio, ChatMessage, Document, Hashtag, Image, LanguageTag, Note, Question, Video, isActor, parseLanguageTag };
package/dist/mod.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
2
+ Date.prototype.toTemporalInstant = toTemporalInstant;
3
+ import { AuthorizedLike, AuthorizedReaction, EmojiReact, Like, RawLike, Reaction } from "./reaction.js";
4
+ import { Text, customEmoji, em, hashtag, link, mention, plainText, strong, text } from "./text.js";
5
+ import { Actor, Article, Audio, AuthorizedMessage, AuthorizedSharedMessage, ChatMessage, Document, Hashtag, LanguageTag, Message, MessageClass, MessageShareOptions, MessageVisibility, Note, Question, SharedMessage, Video, isActor, parseLanguageTag } from "./message.js";
6
+ import { Announce, Create, KvRepository, MemoryCachedRepository, MemoryRepository, Repository } from "./repository.js";
7
+ import { Session, SessionGetOutboxOptions, SessionPublishOptions, SessionPublishOptionsWithClass } from "./session.js";
8
+ import { CustomEmoji, DeferredCustomEmoji, Emoji, emoji, isEmoji } from "./emoji.js";
9
+ import { FollowRequest } from "./follow.js";
10
+ import { AcceptEventHandler, FollowEventHandler, LikeEventHandler, MentionEventHandler, MessageEventHandler, QuoteEventHandler, ReactionEventHandler, RejectEventHandler, ReplyEventHandler, SharedMessageEventHandler, UndoneReactionEventHandler, UnfollowEventHandler, UnlikeEventHandler } from "./events.js";
11
+ import { Application, Bot, BotWithVoidContextData, CreateBotOptions, Image, PagesOptions, SemVer, Service, Software, createBot, parseSemVer } from "./bot.js";
12
+ import { InProcessMessageQueue, InProcessMessageQueueOptions, MemoryKvStore, ParallelMessageQueue } from "@fedify/fedify/federation";
13
+ export { AcceptEventHandler, Actor, Announce, Application, Article, Audio, AuthorizedLike, AuthorizedMessage, AuthorizedReaction, AuthorizedSharedMessage, Bot, BotWithVoidContextData, ChatMessage, Create, CreateBotOptions, CustomEmoji, DeferredCustomEmoji, Document, Emoji, EmojiReact, FollowEventHandler, FollowRequest, Hashtag, Image, InProcessMessageQueue, InProcessMessageQueueOptions, KvRepository, LanguageTag, Like, LikeEventHandler, MemoryCachedRepository, MemoryKvStore, MemoryRepository, MentionEventHandler, Message, MessageClass, MessageEventHandler, MessageShareOptions, MessageVisibility, Note, PagesOptions, ParallelMessageQueue, Question, QuoteEventHandler, RawLike, Reaction, ReactionEventHandler, RejectEventHandler, ReplyEventHandler, Repository, SemVer, Service, Session, SessionGetOutboxOptions, SessionPublishOptions, SessionPublishOptionsWithClass, SharedMessage, SharedMessageEventHandler, Software, Text, UndoneReactionEventHandler, UnfollowEventHandler, UnlikeEventHandler, Video, createBot, customEmoji, em, emoji, hashtag, isActor, isEmoji, link, mention, parseLanguageTag, parseSemVer, plainText, strong, text };
package/dist/mod.js ADDED
@@ -0,0 +1,13 @@
1
+
2
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
3
+ Date.prototype.toTemporalInstant = toTemporalInstant;
4
+
5
+ import { emoji, isEmoji } from "./emoji.js";
6
+ import { Announce, Create, KvRepository, MemoryCachedRepository, MemoryRepository } from "./repository.js";
7
+ import { Application, Image, Service, createBot, parseSemVer } from "./bot.js";
8
+ import { customEmoji, em, hashtag, link, mention, plainText, strong, text } from "./text.js";
9
+ import { Article, Audio, ChatMessage, Document, Hashtag, LanguageTag, Note, Question, Video, isActor, parseLanguageTag } from "./message.js";
10
+ import { EmojiReact, RawLike } from "./reaction.js";
11
+ import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue } from "@fedify/fedify/federation";
12
+
13
+ export { Announce, Application, Article, Audio, ChatMessage, Create, Document, EmojiReact, Hashtag, Image, InProcessMessageQueue, KvRepository, LanguageTag, MemoryCachedRepository, MemoryKvStore, MemoryRepository, Note, ParallelMessageQueue, Question, RawLike, Service, Video, createBot, customEmoji, em, emoji, hashtag, isActor, isEmoji, link, mention, parseLanguageTag, parseSemVer, plainText, strong, text };
@@ -0,0 +1,20 @@
1
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
2
+ Date.prototype.toTemporalInstant = toTemporalInstant;
3
+ import { BotImpl } from "./bot-impl.js";
4
+ import { Hono } from "hono";
5
+ import * as hono_types0 from "hono/types";
6
+
7
+ //#region src/pages.d.ts
8
+ interface Bindings {
9
+ readonly bot: BotImpl<unknown>;
10
+ readonly contextData: unknown;
11
+ }
12
+ interface Env {
13
+ readonly Bindings: Bindings;
14
+ }
15
+ declare const app: Hono<Env, hono_types0.BlankSchema, "/">;
16
+ //# sourceMappingURL=pages.d.ts.map
17
+
18
+ //#endregion
19
+ export { Bindings, Env, app };
20
+ //# sourceMappingURL=pages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.d.ts","names":[],"sources":["../src/pages.tsx"],"sourcesContent":[],"mappings":";;;;;;;UAqCiB,QAAA;gBACD;;;UAIC,GAAA;qBACI;;AANJ,cASJ,GATY,EAST,IARA,CAQA,GARO,EAQP,WAAA,CAAA,WAAA,EARO,GAAA,CAAA;AAIvB"}
package/dist/pages.js ADDED
@@ -0,0 +1,361 @@
1
+
2
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
3
+ Date.prototype.toTemporalInstant = toTemporalInstant;
4
+
5
+ import { getMessageClass, isMessageObject, textXss } from "./message-impl.js";
6
+ import { Layout } from "./components/Layout.js";
7
+ import { Message } from "./components/Message.js";
8
+ import { Hashtag, Image, Link, PUBLIC_COLLECTION, getActorHandle } from "@fedify/fedify/vocab";
9
+ import { decode } from "html-entities";
10
+ import { Hono } from "hono";
11
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
12
+
13
+ //#region src/pages.tsx
14
+ const app = new Hono();
15
+ app.get("/", async (c) => {
16
+ const { bot } = c.env;
17
+ const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
18
+ const session = bot.getSession(ctx);
19
+ const url = new URL(c.req.url);
20
+ const handle = `@${bot.username}@${url.host}`;
21
+ const icon = bot.icon instanceof Image ? bot.icon.url instanceof Link ? bot.icon.url.href : bot.icon.url : bot.icon;
22
+ const iconWidth = bot.icon instanceof Image ? bot.icon.width : null;
23
+ const iconHeight = bot.icon instanceof Image ? bot.icon.height : null;
24
+ const image = bot.image instanceof Image ? bot.image.url instanceof Link ? bot.image.url.href : bot.image.url : bot.image;
25
+ const imageWidth = bot.image instanceof Image ? bot.image.width : null;
26
+ const imageHeight = bot.image instanceof Image ? bot.image.height : null;
27
+ const followersCount = await bot.repository.countFollowers();
28
+ const summaryChunks = bot.summary?.getHtml(session);
29
+ const postsCount = await bot.repository.countMessages();
30
+ const summary = summaryChunks == null ? null : (await Array.fromAsync(summaryChunks)).join("");
31
+ const properties = {};
32
+ for (const name in bot.properties) {
33
+ const value = bot.properties[name];
34
+ const valueHtml = (await Array.fromAsync(value.getHtml(session))).join("");
35
+ properties[name] = valueHtml;
36
+ }
37
+ const offset = c.req.query("offset");
38
+ const { posts: messages, nextPost } = await getPosts(bot, ctx, offset ? { offset: Temporal.Instant.from(offset) } : {});
39
+ const activityLink = ctx.getActorUri(bot.identifier);
40
+ const feedLink = new URL("/feed.xml", url);
41
+ let nextLink;
42
+ if (nextPost?.published != null) {
43
+ nextLink = new URL("/", url);
44
+ nextLink.searchParams.set("offset", nextPost.published.toString());
45
+ }
46
+ return c.html(/* @__PURE__ */ jsxs(Layout, {
47
+ bot,
48
+ host: url.host,
49
+ activityLink,
50
+ feedLink,
51
+ children: [
52
+ /* @__PURE__ */ jsxs("header", {
53
+ class: "container",
54
+ children: [
55
+ image && /* @__PURE__ */ jsx("img", {
56
+ src: image.href,
57
+ width: imageWidth ?? void 0,
58
+ height: imageHeight ?? void 0,
59
+ alt: image instanceof Image ? image.name?.toString() ?? void 0 : void 0,
60
+ style: "width: 100%; margin-bottom: 1em;"
61
+ }),
62
+ /* @__PURE__ */ jsxs("hgroup", { children: [
63
+ icon && /* @__PURE__ */ jsx("img", {
64
+ src: icon.href,
65
+ width: iconWidth ?? void 0,
66
+ height: iconHeight ?? void 0,
67
+ style: "float: left; margin-right: 1em; height: 72;"
68
+ }),
69
+ /* @__PURE__ */ jsx("h1", { children: /* @__PURE__ */ jsx("a", {
70
+ href: "/",
71
+ children: bot.name ?? bot.username
72
+ }) }),
73
+ /* @__PURE__ */ jsxs("p", { children: [
74
+ /* @__PURE__ */ jsx("span", {
75
+ style: "user-select: all;",
76
+ children: handle
77
+ }),
78
+ " ·",
79
+ " ",
80
+ /* @__PURE__ */ jsx("a", {
81
+ href: "/feed.xml",
82
+ rel: "alternate",
83
+ type: "application/atom+xml",
84
+ title: "Atom feed",
85
+ children: /* @__PURE__ */ jsx("svg", {
86
+ xmlns: "http://www.w3.org/2000/svg",
87
+ width: 18,
88
+ height: 18,
89
+ viewBox: "0 0 16 16",
90
+ "aria-label": "Atom feed",
91
+ children: /* @__PURE__ */ jsx("path", {
92
+ fill: "currentColor",
93
+ d: "M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0a8 8 0 0 0-8-8a1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6a1 1 0 1 1-2 0a4 4 0 0 0-4-4a1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3a1.5 1.5 0 0 1 0 3"
94
+ })
95
+ })
96
+ }),
97
+ " ",
98
+ "·",
99
+ " ",
100
+ /* @__PURE__ */ jsx("span", { children: followersCount === 1 ? `1 follower` : `${followersCount.toLocaleString("en")} followers` }),
101
+ " ",
102
+ "·",
103
+ " ",
104
+ /* @__PURE__ */ jsx("span", { children: postsCount === 1 ? `1 post` : `${postsCount.toLocaleString("en")} posts` }),
105
+ " "
106
+ ] })
107
+ ] }),
108
+ summary && /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: summary } }),
109
+ globalThis.Object.keys(properties).length > 0 && /* @__PURE__ */ jsx("table", { children: /* @__PURE__ */ jsx("tbody", { children: globalThis.Object.entries(properties).map(([name, value]) => /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
110
+ scope: "row",
111
+ style: "width: 1%; white-space: nowrap;",
112
+ children: /* @__PURE__ */ jsx("strong", { children: name })
113
+ }), /* @__PURE__ */ jsx("td", { dangerouslySetInnerHTML: { __html: value } })] })) }) })
114
+ ]
115
+ }),
116
+ /* @__PURE__ */ jsx("main", {
117
+ class: "container",
118
+ children: messages.map((message) => /* @__PURE__ */ jsx(Message, {
119
+ message,
120
+ session
121
+ }))
122
+ }),
123
+ /* @__PURE__ */ jsx("footer", {
124
+ class: "container",
125
+ children: /* @__PURE__ */ jsx("nav", {
126
+ style: "display: block; text-align: end;",
127
+ children: nextLink && /* @__PURE__ */ jsx("a", {
128
+ rel: "next",
129
+ href: nextLink.href,
130
+ children: "Older posts →"
131
+ })
132
+ })
133
+ })
134
+ ]
135
+ }), { headers: { Link: `<${activityLink.href}>; rel="alternate"; type="application/activity+json", <${feedLink.href}>; rel="alternate"; type="application/atom+xml"` + (nextLink ? `, <${nextLink.href}>; rel="next"; type="text/html"` : "") } });
136
+ });
137
+ app.get("/tags/:hashtag", async (c) => {
138
+ const hashtag = c.req.param("hashtag");
139
+ const { bot } = c.env;
140
+ const url = new URL(c.req.url);
141
+ const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
142
+ const session = bot.getSession(ctx);
143
+ const offset = c.req.query("offset");
144
+ const { posts, nextPost } = await getPosts(bot, ctx, {
145
+ hashtag,
146
+ offset: offset == null ? void 0 : Temporal.Instant.from(offset)
147
+ });
148
+ let nextLink;
149
+ if (nextPost?.published != null) {
150
+ nextLink = new URL(`/tags/${encodeURIComponent(hashtag)}`, url);
151
+ nextLink.searchParams.set("offset", nextPost.published.toString());
152
+ }
153
+ return c.html(/* @__PURE__ */ jsxs(Layout, {
154
+ bot,
155
+ host: url.host,
156
+ title: `#${hashtag}`,
157
+ children: [
158
+ /* @__PURE__ */ jsx("header", {
159
+ class: "container",
160
+ children: /* @__PURE__ */ jsxs("h1", { children: ["#", hashtag] })
161
+ }),
162
+ /* @__PURE__ */ jsx("main", {
163
+ class: "container",
164
+ children: posts.map((message) => /* @__PURE__ */ jsx(Message, {
165
+ message,
166
+ session
167
+ }))
168
+ }),
169
+ /* @__PURE__ */ jsx("footer", {
170
+ class: "container",
171
+ children: /* @__PURE__ */ jsx("nav", {
172
+ style: "display: block; text-align: end;",
173
+ children: nextLink && /* @__PURE__ */ jsx("a", {
174
+ rel: "next",
175
+ href: nextLink.href,
176
+ children: "Older posts →"
177
+ })
178
+ })
179
+ })
180
+ ]
181
+ }), { headers: nextLink == null ? {} : { Link: `<${nextLink.href}>; rel="next"; type="text/html"` } });
182
+ });
183
+ app.get("/message/:id", async (c) => {
184
+ const id = c.req.param("id");
185
+ const { bot } = c.env;
186
+ const url = new URL(c.req.url);
187
+ const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
188
+ const session = bot.getSession(ctx);
189
+ const post = await bot.repository.getMessage(id);
190
+ if (post == null || !isPublic(post)) return c.notFound();
191
+ const message = await post.getObject(ctx);
192
+ if (message == null || !isMessageObject(message)) return c.notFound();
193
+ const activityLink = ctx.getObjectUri(getMessageClass(message), { id });
194
+ const feedLink = new URL("/feed.xml", url);
195
+ let title = message.name;
196
+ if (title == null) {
197
+ title = message.summary ?? message.content;
198
+ if (title != null) title = decode(textXss.process(title.toString()));
199
+ }
200
+ return c.html(/* @__PURE__ */ jsx(Layout, {
201
+ bot,
202
+ host: url.host,
203
+ activityLink,
204
+ feedLink,
205
+ title: title?.toString() ?? void 0,
206
+ children: /* @__PURE__ */ jsx("main", {
207
+ class: "container",
208
+ children: /* @__PURE__ */ jsx(Message, {
209
+ message,
210
+ session
211
+ })
212
+ })
213
+ }), { headers: { Link: `<${activityLink.href}>; rel="alternate"; type="application/activity+json", <${feedLink.href}>; rel="alternate"; type="application/atom+xml"` } });
214
+ });
215
+ app.get("/feed.xml", async (c) => {
216
+ const { bot } = c.env;
217
+ const url = new URL(c.req.url);
218
+ const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
219
+ const session = bot.getSession(ctx);
220
+ const { posts } = await getPosts(bot, ctx, { window: 30 });
221
+ const botName = bot.name ?? bot.username;
222
+ const canonicalUrl = new URL("/feed.xml", url);
223
+ const profileUrl = new URL("/", url);
224
+ const actorUrl = ctx.getActorUri(bot.identifier);
225
+ c.header("Link", `<${actorUrl.href}>; rel="alternate"; type="application/activity+json", <${profileUrl.href}>; rel="alternate"; type="text/html"`);
226
+ const response = await c.render(/* @__PURE__ */ jsxs("feed", {
227
+ xmlns: "http://www.w3.org/2005/Atom",
228
+ children: [
229
+ /* @__PURE__ */ jsx("id", { children: canonicalUrl.href }),
230
+ /* @__PURE__ */ jsx("link", {
231
+ rel: "self",
232
+ type: "application/atom+xml",
233
+ href: canonicalUrl.href
234
+ }),
235
+ /* @__PURE__ */ jsx("link", {
236
+ rel: "alternate",
237
+ type: "text/html",
238
+ href: profileUrl.href
239
+ }),
240
+ /* @__PURE__ */ jsx("link", {
241
+ rel: "alternate",
242
+ type: "application/activity+json",
243
+ href: actorUrl.href
244
+ }),
245
+ /* @__PURE__ */ jsxs("title", { children: [
246
+ botName,
247
+ " (@",
248
+ bot.username,
249
+ "@",
250
+ url.host,
251
+ ")"
252
+ ] }),
253
+ /* @__PURE__ */ jsxs("author", { children: [/* @__PURE__ */ jsx("name", { children: botName }), /* @__PURE__ */ jsx("uri", { children: profileUrl.href })] }),
254
+ posts.length > 0 && /* @__PURE__ */ jsx("updated", { children: (posts[0].updated ?? posts[0].published)?.toString() }),
255
+ posts.map(async (post) => {
256
+ const activityUrl = post.id;
257
+ if (activityUrl == null) return void 0;
258
+ const permalink = (post.url instanceof Link ? post.url.href : post.url) ?? activityUrl;
259
+ const author = post.attributionId?.href === session.actorId?.href ? await session.getActor() : await post.getAttribution({
260
+ documentLoader: ctx.documentLoader,
261
+ contextLoader: ctx.contextLoader,
262
+ suppressError: true
263
+ });
264
+ const authorName = author?.name ?? author?.preferredUsername ?? (author == null ? void 0 : await getActorHandle(author));
265
+ const authorUrl = (author?.url instanceof Link ? author.url.href : author?.url) ?? author?.id;
266
+ const updated = post.updated ?? post.published;
267
+ let title = post.name;
268
+ if (title == null) {
269
+ title = post.summary ?? post.content;
270
+ if (title != null) title = decode(textXss.process(title.toString()));
271
+ }
272
+ return /* @__PURE__ */ jsxs("entry", { children: [
273
+ /* @__PURE__ */ jsx("id", { children: permalink.href }),
274
+ /* @__PURE__ */ jsx("link", {
275
+ rel: "alternate",
276
+ type: "text/html",
277
+ href: permalink.href
278
+ }),
279
+ /* @__PURE__ */ jsx("link", {
280
+ rel: "alternate",
281
+ type: "application/activity+json",
282
+ href: activityUrl.href
283
+ }),
284
+ authorName && /* @__PURE__ */ jsxs("author", { children: [/* @__PURE__ */ jsx("name", { children: authorName }), authorUrl && /* @__PURE__ */ jsx("uri", { children: authorUrl.href })] }),
285
+ post.published && /* @__PURE__ */ jsx("published", { children: post.published.toString() }),
286
+ updated && /* @__PURE__ */ jsx("updated", { children: updated.toString() }),
287
+ title && /* @__PURE__ */ jsx("title", { children: title }),
288
+ post.summary && /* @__PURE__ */ jsx("summary", {
289
+ type: "html",
290
+ children: post.summary.toString()
291
+ }),
292
+ post.content && /* @__PURE__ */ jsx("content", {
293
+ type: "html",
294
+ children: post.content.toString()
295
+ })
296
+ ] });
297
+ })
298
+ ]
299
+ }));
300
+ response.headers.set("Content-Type", "application/atom+xml; charset=utf-8");
301
+ return response;
302
+ });
303
+ async function getPosts(bot, ctx, options = {}) {
304
+ const { offset, window = 15 } = options;
305
+ let posts = await Array.fromAsync(bot.repository.getMessages({
306
+ order: "newest",
307
+ until: offset,
308
+ limit: window * 2
309
+ }));
310
+ let lastPost = posts[posts.length - 1];
311
+ posts = posts.slice(0, posts.length - 1);
312
+ posts = posts.filter(isPublic);
313
+ if (options.hashtag != null) {
314
+ const taggedPosts = [];
315
+ for (const post of posts) if (await hasHashtag(ctx, post, options.hashtag)) taggedPosts.push(post);
316
+ posts = taggedPosts;
317
+ }
318
+ while (lastPost != null && posts.length < window) {
319
+ const limit = (window - posts.length) * 2;
320
+ const until = lastPost.published ?? (await lastPost.getObject(ctx))?.published ?? void 0;
321
+ if (until == null) break;
322
+ const nextPosts = bot.repository.getMessages({
323
+ order: "newest",
324
+ until,
325
+ limit
326
+ });
327
+ let i = 0;
328
+ lastPost = void 0;
329
+ for await (const post of nextPosts) {
330
+ if (isPublic(post) && await hasHashtag(ctx, post, options.hashtag) && posts.length < window + 1) posts.push(post);
331
+ lastPost = post;
332
+ i++;
333
+ }
334
+ if (i < limit) break;
335
+ }
336
+ const nextPost = await posts[window]?.getObject(ctx) ?? void 0;
337
+ posts = posts.slice(0, window);
338
+ const messages = (await Promise.all(posts.map((p) => p.getObject(ctx)))).filter(isMessageObject);
339
+ return {
340
+ posts: messages,
341
+ nextPost
342
+ };
343
+ }
344
+ function isPublic(post) {
345
+ return post.toIds.some((url) => url.href === PUBLIC_COLLECTION.href) || post.ccIds.some((url) => url.href === PUBLIC_COLLECTION.href);
346
+ }
347
+ async function hasHashtag(context, post, hashtag) {
348
+ if (hashtag == null) return true;
349
+ hashtag = normalizeHashtag(hashtag);
350
+ const object = await post.getObject(context);
351
+ if (object == null) return false;
352
+ for await (const tag of object.getTags(context)) if (tag instanceof Hashtag && tag.name != null && normalizeHashtag(tag.name.toString()) === hashtag) return true;
353
+ return false;
354
+ }
355
+ function normalizeHashtag(hashtag) {
356
+ return hashtag.toLowerCase().trimStart().replace(/^#/, "").trim().replace(/\s+/g, "");
357
+ }
358
+
359
+ //#endregion
360
+ export { app };
361
+ //# sourceMappingURL=pages.js.map