birdclaw 0.8.2 → 0.8.3
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.
- package/CHANGELOG.md +16 -0
- package/package.json +2 -1
- package/src/cli/command-context.ts +17 -0
- package/src/cli/register-analysis.ts +500 -0
- package/src/cli/register-compose.ts +40 -0
- package/src/cli/register-graph.ts +132 -0
- package/src/cli/register-inbox.ts +41 -0
- package/src/cli/register-storage.ts +106 -0
- package/src/cli.ts +30 -750
- package/src/components/AccountSwitcher.tsx +7 -15
- package/src/components/AvatarChip.tsx +1 -1
- package/src/components/AvatarPreload.ts +149 -0
- package/src/components/MarkdownCitations.tsx +680 -0
- package/src/components/MarkdownViewer.tsx +8 -674
- package/src/components/ProfileAnalysisClient.ts +191 -0
- package/src/components/ProfileAnalysisStream.tsx +16 -185
- package/src/components/ProfilePreview.tsx +2 -0
- package/src/components/links-controller.ts +162 -0
- package/src/components/links-model.ts +198 -0
- package/src/components/network-map-controller.ts +84 -0
- package/src/components/network-map-model.ts +255 -0
- package/src/components/useTimelineRouteData.ts +105 -235
- package/src/lib/analysis-runtime.ts +238 -0
- package/src/lib/api-client.ts +16 -215
- package/src/lib/api-contracts.ts +328 -0
- package/src/lib/archive-import-plan.ts +102 -0
- package/src/lib/archive-import.ts +170 -239
- package/src/lib/authored-live.ts +75 -120
- package/src/lib/backup.ts +335 -424
- package/src/lib/blocks-write.ts +30 -26
- package/src/lib/blocks.ts +18 -20
- package/src/lib/database-metrics.ts +88 -0
- package/src/lib/database-migrations.ts +34 -0
- package/src/lib/database-schema.ts +312 -0
- package/src/lib/database-writer.ts +69 -0
- package/src/lib/db.ts +84 -330
- package/src/lib/dm-read-model.ts +533 -0
- package/src/lib/dms-live.ts +34 -97
- package/src/lib/follow-graph.ts +17 -27
- package/src/lib/import-repository.ts +138 -0
- package/src/lib/inbox.ts +2 -1
- package/src/lib/live-sync-engine.ts +209 -0
- package/src/lib/live-transport-gateway.ts +128 -0
- package/src/lib/mention-threads-live.ts +90 -177
- package/src/lib/mentions-export.ts +1 -1
- package/src/lib/mentions-live.ts +57 -181
- package/src/lib/moderation-target.ts +15 -4
- package/src/lib/moderation-write.ts +1 -1
- package/src/lib/mutes-write.ts +30 -26
- package/src/lib/openai-response-runtime.ts +251 -0
- package/src/lib/paginated-sync.ts +93 -0
- package/src/lib/period-digest.ts +116 -304
- package/src/lib/profile-analysis.ts +36 -110
- package/src/lib/queries.ts +6 -2381
- package/src/lib/query-actions.ts +437 -0
- package/src/lib/query-client.tsx +47 -0
- package/src/lib/query-read-model-shared.ts +52 -0
- package/src/lib/query-read-models.ts +5 -0
- package/src/lib/query-resource.ts +41 -0
- package/src/lib/query-status.ts +164 -0
- package/src/lib/research.ts +1 -1
- package/src/lib/runtime-services.ts +20 -0
- package/src/lib/search-discussion.ts +75 -279
- package/src/lib/server-runtime-services.ts +30 -0
- package/src/lib/sqlite.ts +48 -12
- package/src/lib/streaming-ingestion.ts +240 -0
- package/src/lib/sync-cache.ts +6 -1
- package/src/lib/sync-plan.ts +175 -0
- package/src/lib/timeline-collections-live.ts +83 -257
- package/src/lib/timeline-live.ts +86 -236
- package/src/lib/timeline-read-model.ts +1191 -0
- package/src/lib/tweet-repository.ts +156 -0
- package/src/lib/tweet-search-live.ts +63 -167
- package/src/lib/web-sync.ts +67 -50
- package/src/lib/whois.ts +2 -1
- package/src/routes/__root.tsx +11 -8
- package/src/routes/api/action.tsx +1 -1
- package/src/routes/api/conversation.tsx +1 -1
- package/src/routes/api/query.tsx +32 -26
- package/src/routes/api/status.tsx +6 -4
- package/src/routes/api/sync.tsx +5 -2
- package/src/routes/blocks.tsx +97 -131
- package/src/routes/data-sources.tsx +17 -25
- package/src/routes/dms.tsx +167 -184
- package/src/routes/inbox.tsx +63 -57
- package/src/routes/links.tsx +31 -394
- package/src/routes/network-map.tsx +41 -344
- package/src/routes/rate-limits.tsx +17 -21
- package/src/lib/client-cache.ts +0 -109
package/src/lib/mentions-live.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import type { Database } from "./sqlite";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import {
|
|
4
|
-
getAuthenticatedBirdAccountEffect,
|
|
5
|
-
listMentionsViaBirdEffect,
|
|
6
|
-
} from "./bird";
|
|
7
3
|
import type { MentionsDataSource } from "./config";
|
|
4
|
+
import { databaseWriteEffect } from "./database-writer";
|
|
8
5
|
import { getNativeDb } from "./db";
|
|
9
|
-
import { runEffectPromise
|
|
10
|
-
import {
|
|
6
|
+
import { runEffectPromise } from "./effect-runtime";
|
|
7
|
+
import { liveTransportGateway } from "./live-transport-gateway";
|
|
8
|
+
import {
|
|
9
|
+
assertLiveAccountMatches,
|
|
10
|
+
resolveLiveSyncAccount,
|
|
11
|
+
type LiveSyncAccount,
|
|
12
|
+
} from "./live-sync-engine";
|
|
11
13
|
import { serializeMentionItemsAsXurlCompatible } from "./mentions-export";
|
|
12
|
-
import { listTimelineItems } from "./
|
|
14
|
+
import { listTimelineItems } from "./timeline-read-model";
|
|
13
15
|
import { deleteSyncCache, readSyncCache, writeSyncCache } from "./sync-cache";
|
|
14
|
-
import { tweetEntitiesFromXurl } from "./tweet-render";
|
|
15
16
|
import type {
|
|
16
17
|
ReplyFilter,
|
|
17
|
-
TweetEntities,
|
|
18
18
|
XurlMentionData,
|
|
19
19
|
XurlMentionsResponse,
|
|
20
20
|
XurlMediaItem,
|
|
21
21
|
XurlMentionUser,
|
|
22
22
|
} from "./types";
|
|
23
|
-
import {
|
|
24
|
-
import { ensureStubProfileForXUser, upsertProfileFromXUser } from "./x-profile";
|
|
25
|
-
import { listMentionsViaXurl, lookupUsersByHandles } from "./xurl";
|
|
23
|
+
import { ingestTweetPayload } from "./tweet-repository";
|
|
26
24
|
|
|
27
25
|
export const DEFAULT_MENTIONS_CACHE_TTL_MS = 2 * 60_000;
|
|
28
26
|
const MIN_XURL_MENTIONS_LIMIT = 5;
|
|
@@ -431,71 +429,21 @@ function writeMentionHighWaterId(
|
|
|
431
429
|
writeSyncCache(getMentionHighWaterKey({ mode, accountId }), { sinceId }, db);
|
|
432
430
|
}
|
|
433
431
|
|
|
434
|
-
function
|
|
435
|
-
const row = accountId
|
|
436
|
-
? (db
|
|
437
|
-
.prepare(
|
|
438
|
-
"select id, handle, external_user_id from accounts where id = ?",
|
|
439
|
-
)
|
|
440
|
-
.get(accountId) as
|
|
441
|
-
| { id: string; handle: string; external_user_id: string | null }
|
|
442
|
-
| undefined)
|
|
443
|
-
: (db
|
|
444
|
-
.prepare(
|
|
445
|
-
`
|
|
446
|
-
select id, handle, external_user_id
|
|
447
|
-
from accounts
|
|
448
|
-
order by is_default desc, created_at asc
|
|
449
|
-
limit 1
|
|
450
|
-
`,
|
|
451
|
-
)
|
|
452
|
-
.get() as
|
|
453
|
-
| { id: string; handle: string; external_user_id: string | null }
|
|
454
|
-
| undefined);
|
|
455
|
-
|
|
456
|
-
if (!row) {
|
|
457
|
-
throw new Error(`Unknown account: ${accountId ?? "default"}`);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
return {
|
|
461
|
-
accountId: row.id,
|
|
462
|
-
username: row.handle.replace(/^@/, ""),
|
|
463
|
-
externalUserId:
|
|
464
|
-
typeof row.external_user_id === "string" &&
|
|
465
|
-
row.external_user_id.trim().length > 0
|
|
466
|
-
? row.external_user_id.trim()
|
|
467
|
-
: undefined,
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function verifyBirdAccountMatchesEffect({
|
|
472
|
-
accountId,
|
|
473
|
-
username,
|
|
474
|
-
externalUserId,
|
|
475
|
-
}: ReturnType<typeof resolveAccount>) {
|
|
432
|
+
function verifyBirdAccountMatchesEffect(account: LiveSyncAccount) {
|
|
476
433
|
return Effect.gen(function* () {
|
|
477
|
-
const authenticated =
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
if (authenticated.username.toLowerCase() !== username.toLowerCase()) {
|
|
493
|
-
return yield* Effect.fail(
|
|
494
|
-
new Error(
|
|
495
|
-
`bird is authenticated as @${authenticated.username}; refusing to sync into ${accountId} (@${username})`,
|
|
496
|
-
),
|
|
497
|
-
);
|
|
498
|
-
}
|
|
434
|
+
const authenticated =
|
|
435
|
+
yield* liveTransportGateway.bird.getAuthenticatedAccount();
|
|
436
|
+
return yield* Effect.try({
|
|
437
|
+
try: () =>
|
|
438
|
+
assertLiveAccountMatches({
|
|
439
|
+
source: "bird",
|
|
440
|
+
account,
|
|
441
|
+
liveUsername: authenticated.username,
|
|
442
|
+
liveExternalUserId: authenticated.id,
|
|
443
|
+
}),
|
|
444
|
+
catch: (error) =>
|
|
445
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
446
|
+
});
|
|
499
447
|
});
|
|
500
448
|
}
|
|
501
449
|
|
|
@@ -521,94 +469,20 @@ function findNewestArchiveMentionId(db: Database, accountId: string) {
|
|
|
521
469
|
return row?.id;
|
|
522
470
|
}
|
|
523
471
|
|
|
524
|
-
function toLocalEntities(tweet: XurlMentionData): TweetEntities {
|
|
525
|
-
return tweetEntitiesFromXurl(tweet.entities);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
function replaceTweetFts(db: Database, tweetId: string, text: string) {
|
|
529
|
-
db.prepare("delete from tweets_fts where tweet_id = ?").run(tweetId);
|
|
530
|
-
db.prepare("insert into tweets_fts (tweet_id, text) values (?, ?)").run(
|
|
531
|
-
tweetId,
|
|
532
|
-
text,
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
472
|
function mergeMentionsIntoLocalStore(
|
|
537
473
|
db: Database,
|
|
538
474
|
accountId: string,
|
|
539
475
|
payload: XurlMentionsResponse,
|
|
540
476
|
source: MentionsDataSource,
|
|
541
477
|
) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
is_replied, reply_to_id, like_count, media_count, bookmarked, liked,
|
|
550
|
-
entities_json, media_json, quoted_tweet_id
|
|
551
|
-
) values (?, ?, ?, 'mention', ?, ?, 0, null, ?, ?, 0, 0, ?, ?, null)
|
|
552
|
-
on conflict(id) do update set
|
|
553
|
-
account_id = tweets.account_id,
|
|
554
|
-
author_profile_id = excluded.author_profile_id,
|
|
555
|
-
kind = case
|
|
556
|
-
when tweets.kind in ('authored', 'home', 'mention') then tweets.kind
|
|
557
|
-
when excluded.kind in ('authored', 'home', 'mention') then excluded.kind
|
|
558
|
-
else excluded.kind
|
|
559
|
-
end,
|
|
560
|
-
text = excluded.text,
|
|
561
|
-
created_at = excluded.created_at,
|
|
562
|
-
like_count = excluded.like_count,
|
|
563
|
-
media_count = max(tweets.media_count, excluded.media_count),
|
|
564
|
-
entities_json = excluded.entities_json,
|
|
565
|
-
media_json = case
|
|
566
|
-
when excluded.media_json not in ('', '[]', 'null') then excluded.media_json
|
|
567
|
-
else tweets.media_json
|
|
568
|
-
end,
|
|
569
|
-
is_replied = max(tweets.is_replied, excluded.is_replied),
|
|
570
|
-
bookmarked = max(tweets.bookmarked, excluded.bookmarked),
|
|
571
|
-
liked = max(tweets.liked, excluded.liked)
|
|
572
|
-
`,
|
|
573
|
-
);
|
|
574
|
-
|
|
575
|
-
const writePayload = db.transaction(() => {
|
|
576
|
-
const seenAt = new Date().toISOString();
|
|
577
|
-
for (const tweet of payload.data) {
|
|
578
|
-
const author =
|
|
579
|
-
usersById.get(tweet.author_id) ??
|
|
580
|
-
({
|
|
581
|
-
id: tweet.author_id,
|
|
582
|
-
username: `user_${tweet.author_id}`,
|
|
583
|
-
name: `user_${tweet.author_id}`,
|
|
584
|
-
} as const);
|
|
585
|
-
const profile = usersById.has(tweet.author_id)
|
|
586
|
-
? upsertProfileFromXUser(db, author)
|
|
587
|
-
: ensureStubProfileForXUser(db, tweet.author_id);
|
|
588
|
-
upsertTweet.run(
|
|
589
|
-
tweet.id,
|
|
590
|
-
accountId,
|
|
591
|
-
profile.profile.id,
|
|
592
|
-
tweet.text,
|
|
593
|
-
tweet.created_at,
|
|
594
|
-
Number(tweet.public_metrics?.like_count ?? 0),
|
|
595
|
-
countTweetMedia(tweet),
|
|
596
|
-
JSON.stringify(toLocalEntities(tweet)),
|
|
597
|
-
buildMediaJsonFromIncludes(tweet, payload.includes?.media),
|
|
598
|
-
);
|
|
599
|
-
upsertTweetAccountEdge(db, {
|
|
600
|
-
accountId,
|
|
601
|
-
tweetId: tweet.id,
|
|
602
|
-
kind: "mention",
|
|
603
|
-
source,
|
|
604
|
-
seenAt,
|
|
605
|
-
rawJson: JSON.stringify(tweet),
|
|
606
|
-
});
|
|
607
|
-
replaceTweetFts(db, tweet.id, tweet.text);
|
|
608
|
-
}
|
|
478
|
+
ingestTweetPayload(db, {
|
|
479
|
+
accountId,
|
|
480
|
+
payload,
|
|
481
|
+
kind: "mention",
|
|
482
|
+
edgeKind: "mention",
|
|
483
|
+
replaceSecondaryKind: true,
|
|
484
|
+
source,
|
|
609
485
|
});
|
|
610
|
-
|
|
611
|
-
writePayload();
|
|
612
486
|
}
|
|
613
487
|
|
|
614
488
|
function shouldReturnFilteredLocalPayload({
|
|
@@ -711,7 +585,7 @@ function fetchMentionsViaXurlEffect({
|
|
|
711
585
|
startTime,
|
|
712
586
|
onProgress,
|
|
713
587
|
}: {
|
|
714
|
-
resolvedAccount:
|
|
588
|
+
resolvedAccount: LiveSyncAccount;
|
|
715
589
|
limit: number;
|
|
716
590
|
all: boolean;
|
|
717
591
|
parsedMaxPages: number | null;
|
|
@@ -723,9 +597,9 @@ function fetchMentionsViaXurlEffect({
|
|
|
723
597
|
return Effect.gen(function* () {
|
|
724
598
|
const accountUserId =
|
|
725
599
|
resolvedAccount.externalUserId ??
|
|
726
|
-
(yield*
|
|
727
|
-
lookupUsersByHandles([resolvedAccount.username])
|
|
728
|
-
|
|
600
|
+
(yield* liveTransportGateway.xurl
|
|
601
|
+
.lookupUsersByHandles([resolvedAccount.username])
|
|
602
|
+
.pipe(Effect.map((users) => users[0]?.id)));
|
|
729
603
|
if (!accountUserId) {
|
|
730
604
|
return yield* Effect.fail(
|
|
731
605
|
new Error(
|
|
@@ -738,16 +612,14 @@ function fetchMentionsViaXurlEffect({
|
|
|
738
612
|
let nextToken: string | undefined = startPaginationToken;
|
|
739
613
|
let pageCount = 0;
|
|
740
614
|
do {
|
|
741
|
-
const payload = yield*
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}),
|
|
750
|
-
);
|
|
615
|
+
const payload = yield* liveTransportGateway.xurl.listMentions({
|
|
616
|
+
maxResults: limit,
|
|
617
|
+
username: resolvedAccount.username,
|
|
618
|
+
userId: String(accountUserId),
|
|
619
|
+
paginationToken: nextToken,
|
|
620
|
+
...(sinceId ? { sinceId } : {}),
|
|
621
|
+
...(startTime ? { startTime } : {}),
|
|
622
|
+
});
|
|
751
623
|
pages.push(payload);
|
|
752
624
|
const metaNextToken =
|
|
753
625
|
typeof payload.meta?.next_token === "string"
|
|
@@ -782,7 +654,7 @@ function fetchMentionsViaXurlEffect({
|
|
|
782
654
|
}
|
|
783
655
|
|
|
784
656
|
function fetchMentionsViaBirdEffect({ limit }: { limit: number }) {
|
|
785
|
-
return
|
|
657
|
+
return liveTransportGateway.bird.listMentions({ maxResults: limit });
|
|
786
658
|
}
|
|
787
659
|
|
|
788
660
|
function isMaxPagesPartial({
|
|
@@ -832,7 +704,9 @@ export function syncMentionsEffect({
|
|
|
832
704
|
(parsedMaxPages !== null ||
|
|
833
705
|
Boolean(explicitSinceId || explicitStartTime));
|
|
834
706
|
const db = yield* trySync(() => getNativeDb());
|
|
835
|
-
const resolvedAccount = yield* trySync(() =>
|
|
707
|
+
const resolvedAccount = yield* trySync(() =>
|
|
708
|
+
resolveLiveSyncAccount(db, account),
|
|
709
|
+
);
|
|
836
710
|
const cursorShape: MentionScanShape = {
|
|
837
711
|
endpoint: "mentions",
|
|
838
712
|
mode: primaryMode,
|
|
@@ -922,9 +796,9 @@ export function syncMentionsEffect({
|
|
|
922
796
|
cached &&
|
|
923
797
|
cacheAgeMs <= ttlMs
|
|
924
798
|
) {
|
|
925
|
-
yield*
|
|
799
|
+
yield* databaseWriteEffect((writeDb) =>
|
|
926
800
|
mergeMentionsIntoLocalStore(
|
|
927
|
-
|
|
801
|
+
writeDb,
|
|
928
802
|
resolvedAccount.accountId,
|
|
929
803
|
cached.value,
|
|
930
804
|
primaryMode,
|
|
@@ -1002,9 +876,9 @@ export function syncMentionsEffect({
|
|
|
1002
876
|
}),
|
|
1003
877
|
);
|
|
1004
878
|
}
|
|
1005
|
-
yield*
|
|
879
|
+
yield* databaseWriteEffect((writeDb) =>
|
|
1006
880
|
mergeMentionsIntoLocalStore(
|
|
1007
|
-
|
|
881
|
+
writeDb,
|
|
1008
882
|
resolvedAccount.accountId,
|
|
1009
883
|
payload,
|
|
1010
884
|
source,
|
|
@@ -1113,7 +987,9 @@ function exportMentionsViaCachedLiveSourceEffect({
|
|
|
1113
987
|
const fetchAll = primaryMode === "xurl" && (all || parsedMaxPages !== null);
|
|
1114
988
|
|
|
1115
989
|
const db = yield* trySync(() => getNativeDb());
|
|
1116
|
-
const resolvedAccount = yield* trySync(() =>
|
|
990
|
+
const resolvedAccount = yield* trySync(() =>
|
|
991
|
+
resolveLiveSyncAccount(db, account),
|
|
992
|
+
);
|
|
1117
993
|
const cacheKey = getMentionsFetchModeKey({
|
|
1118
994
|
scope: "export",
|
|
1119
995
|
mode,
|
|
@@ -1171,14 +1047,14 @@ function exportMentionsViaCachedLiveSourceEffect({
|
|
|
1171
1047
|
);
|
|
1172
1048
|
}),
|
|
1173
1049
|
Effect.flatMap((payload) =>
|
|
1174
|
-
|
|
1050
|
+
databaseWriteEffect((writeDb) => {
|
|
1175
1051
|
mergeMentionsIntoLocalStore(
|
|
1176
|
-
|
|
1052
|
+
writeDb,
|
|
1177
1053
|
resolvedAccount.accountId,
|
|
1178
1054
|
payload,
|
|
1179
1055
|
source,
|
|
1180
1056
|
);
|
|
1181
|
-
writeSyncCache(cacheKey, payload,
|
|
1057
|
+
writeSyncCache(cacheKey, payload, writeDb);
|
|
1182
1058
|
return readFilteredOrRaw(payload);
|
|
1183
1059
|
}),
|
|
1184
1060
|
),
|
|
@@ -2,6 +2,7 @@ import type { Database } from "./sqlite";
|
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
import { lookupProfileViaBird } from "./bird-actions";
|
|
4
4
|
import { getNativeDb } from "./db";
|
|
5
|
+
import { databaseWriteEffect } from "./database-writer";
|
|
5
6
|
import { runEffectPromise, tryPromise } from "./effect-runtime";
|
|
6
7
|
import type { ProfileRecord, XurlMentionUser } from "./types";
|
|
7
8
|
import { getExternalUserId, upsertProfileFromXUser } from "./x-profile";
|
|
@@ -105,9 +106,10 @@ export function resolveLocalProfile(
|
|
|
105
106
|
|
|
106
107
|
export function resolveProfileEffect(
|
|
107
108
|
query: string,
|
|
109
|
+
providedDb?: Database,
|
|
108
110
|
): Effect.Effect<ResolvedModerationProfile, unknown> {
|
|
109
111
|
return Effect.gen(function* () {
|
|
110
|
-
const db = yield* trySync(() => getNativeDb());
|
|
112
|
+
const db = providedDb ?? (yield* trySync(() => getNativeDb()));
|
|
111
113
|
const normalizedQuery = normalizeProfileQuery(query);
|
|
112
114
|
if (!normalizedQuery) {
|
|
113
115
|
return yield* Effect.fail(new Error("Missing profile handle or id"));
|
|
@@ -180,7 +182,10 @@ export function resolveProfileEffect(
|
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
if (local) {
|
|
183
|
-
return yield*
|
|
185
|
+
return yield* databaseWriteEffect(
|
|
186
|
+
(writeDb) => upsertProfileFromXUser(writeDb, user),
|
|
187
|
+
db,
|
|
188
|
+
);
|
|
184
189
|
}
|
|
185
190
|
|
|
186
191
|
const username = String(user.username ?? "").replace(/^@/, "");
|
|
@@ -189,11 +194,17 @@ export function resolveProfileEffect(
|
|
|
189
194
|
resolveLocalProfile(db, username),
|
|
190
195
|
);
|
|
191
196
|
if (localByHandle) {
|
|
192
|
-
return yield*
|
|
197
|
+
return yield* databaseWriteEffect(
|
|
198
|
+
(writeDb) => upsertProfileFromXUser(writeDb, user),
|
|
199
|
+
db,
|
|
200
|
+
);
|
|
193
201
|
}
|
|
194
202
|
}
|
|
195
203
|
|
|
196
|
-
return yield*
|
|
204
|
+
return yield* databaseWriteEffect(
|
|
205
|
+
(writeDb) => upsertProfileFromXUser(writeDb, user),
|
|
206
|
+
db,
|
|
207
|
+
);
|
|
197
208
|
});
|
|
198
209
|
}
|
|
199
210
|
|
|
@@ -53,7 +53,7 @@ export function resolveModerationTargetEffect({
|
|
|
53
53
|
return yield* Effect.fail(new Error(selfActionError));
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const resolved = yield* resolveProfileEffect(query);
|
|
56
|
+
const resolved = yield* resolveProfileEffect(query, db);
|
|
57
57
|
const account = yield* trySync(
|
|
58
58
|
() =>
|
|
59
59
|
db
|
package/src/lib/mutes-write.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { runModerationAction } from "./actions-transport";
|
|
3
|
+
import { databaseWriteEffect } from "./database-writer";
|
|
3
4
|
import { runEffectPromise, tryPromise } from "./effect-runtime";
|
|
4
5
|
import {
|
|
5
6
|
deleteModerationRow,
|
|
@@ -8,14 +9,6 @@ import {
|
|
|
8
9
|
writeModerationRow,
|
|
9
10
|
} from "./moderation-write";
|
|
10
11
|
|
|
11
|
-
function trySync<T>(try_: () => T) {
|
|
12
|
-
return Effect.try({
|
|
13
|
-
try: try_,
|
|
14
|
-
catch: (error) =>
|
|
15
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
12
|
export function addMuteEffect(
|
|
20
13
|
accountId: string,
|
|
21
14
|
query: string,
|
|
@@ -49,14 +42,16 @@ export function addMuteEffect(
|
|
|
49
42
|
}
|
|
50
43
|
|
|
51
44
|
const mutedAt = new Date().toISOString();
|
|
52
|
-
yield*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
yield* databaseWriteEffect(
|
|
46
|
+
(writeDb) =>
|
|
47
|
+
writeModerationRow(
|
|
48
|
+
writeDb,
|
|
49
|
+
"mutes",
|
|
50
|
+
resolvedAccountId,
|
|
51
|
+
resolved.profile.id,
|
|
52
|
+
mutedAt,
|
|
53
|
+
),
|
|
54
|
+
db,
|
|
60
55
|
);
|
|
61
56
|
|
|
62
57
|
return {
|
|
@@ -88,14 +83,16 @@ export function recordMuteEffect(accountId: string, query: string) {
|
|
|
88
83
|
});
|
|
89
84
|
|
|
90
85
|
const mutedAt = new Date().toISOString();
|
|
91
|
-
yield*
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
86
|
+
yield* databaseWriteEffect(
|
|
87
|
+
(writeDb) =>
|
|
88
|
+
writeModerationRow(
|
|
89
|
+
writeDb,
|
|
90
|
+
"mutes",
|
|
91
|
+
resolvedAccountId,
|
|
92
|
+
resolved.profile.id,
|
|
93
|
+
mutedAt,
|
|
94
|
+
),
|
|
95
|
+
db,
|
|
99
96
|
);
|
|
100
97
|
|
|
101
98
|
return {
|
|
@@ -144,8 +141,15 @@ export function removeMuteEffect(
|
|
|
144
141
|
};
|
|
145
142
|
}
|
|
146
143
|
|
|
147
|
-
yield*
|
|
148
|
-
|
|
144
|
+
yield* databaseWriteEffect(
|
|
145
|
+
(writeDb) =>
|
|
146
|
+
deleteModerationRow(
|
|
147
|
+
writeDb,
|
|
148
|
+
"mutes",
|
|
149
|
+
resolvedAccountId,
|
|
150
|
+
resolved.profile.id,
|
|
151
|
+
),
|
|
152
|
+
db,
|
|
149
153
|
);
|
|
150
154
|
|
|
151
155
|
return {
|