diff --git a/keyserver/src/responders/farcaster-webhook-responders.js b/keyserver/src/responders/farcaster-webhook-responders.js --- a/keyserver/src/responders/farcaster-webhook-responders.js +++ b/keyserver/src/responders/farcaster-webhook-responders.js @@ -2,10 +2,13 @@ import { createHmac } from 'crypto'; import type { $Request } from 'express'; +import invariant from 'invariant'; import bots from 'lib/facts/bots.js'; import { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js'; +import { createSidebarThreadName } from 'lib/shared/sidebar-utils.js'; import { type NeynarWebhookCastCreatedEvent } from 'lib/types/farcaster-types.js'; +import { messageTypes } from 'lib/types/message-types-enum.js'; import { threadTypes } from 'lib/types/thread-types-enum.js'; import { type NewThreadResponse } from 'lib/types/thread-types.js'; import { neynarWebhookCastCreatedEventValidator } from 'lib/types/validators/farcaster-webhook-validators.js'; @@ -17,10 +20,16 @@ createOrUpdateFarcasterChannelTag, farcasterChannelTagBlobValidator, } from '../creators/farcaster-channel-tag-creator.js'; +import createMessages from '../creators/message-creator.js'; import { createThread } from '../creators/thread-creator.js'; import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js'; import { createBotViewer } from '../session/bots.js'; -import { updateRole } from '../updaters/thread-updaters.js'; +import { createScriptViewer } from '../session/scripts.js'; +import { + changeRole, + commitMembershipChangeset, +} from '../updaters/thread-permission-updaters.js'; +import { updateRole, joinThread } from '../updaters/thread-updaters.js'; import { thisKeyserverAdmin, thisKeyserverID } from '../user/identity.js'; import { getFarcasterBotConfig } from '../utils/farcaster-bot.js'; import { getVerifiedUserIDForFID } from '../utils/farcaster-utils.js'; @@ -34,6 +43,70 @@ const { commbot } = bots; const commbotViewer = createBotViewer(commbot.userID); +async function createCastSidebar( + sidebarCastHash: string, + channelName: ?string, + channelCommunityID: string, + taggerUserID: ?string, +): Promise { + const sidebarCast = + await neynarClient?.fetchFarcasterCastByHash(sidebarCastHash); + + if (!sidebarCast) { + return null; + } + + const castAuthor = sidebarCast.author.username; + const channelText = channelName ? ` in channel ${channelName}` : ''; + const messageText = `${castAuthor} said "${sidebarCast.text}"${channelText}`; + + let viewer = commbotViewer; + if (taggerUserID) { + viewer = createScriptViewer(taggerUserID); + await joinThread(viewer, { + threadID: channelCommunityID, + }); + } else { + const changeset = await changeRole( + channelCommunityID, + [commbot.userID], + -1, + ); + await commitMembershipChangeset(viewer, changeset); + } + + const [{ id: messageID }] = await createMessages(viewer, [ + { + type: messageTypes.TEXT, + threadID: channelCommunityID, + creatorID: viewer.id, + time: Date.now(), + text: messageText, + }, + ]); + + invariant( + messageID, + 'message returned from createMessages always has ID set', + ); + const sidebarThreadName = createSidebarThreadName(messageText); + + const response = await createThread( + viewer, + { + type: threadTypes.SIDEBAR, + parentThreadID: channelCommunityID, + name: sidebarThreadName, + sourceMessageID: messageID, + }, + { + addViewerAsGhost: !taggerUserID, + }, + ); + + return response; +} + async function createTaggedFarcasterCommunity( channelID: string, taggerUserID: ?string, @@ -198,7 +271,24 @@ throw new ServerError('blob_fetch_failed'); } } - console.log(channelCommunityID); + + const { hash: castHash, parent_hash: parentHash } = event.data; + const taggerUserID = await taggerUserIDPromise; + + // we use the parent cast to create the sidebar source message if it exists + const sidebarCastHash = parentHash ?? castHash; + const sidebarThreadResponse = await createCastSidebar( + sidebarCastHash, + eventChannel?.name, + channelCommunityID, + taggerUserID, + ); + + if (!sidebarThreadResponse) { + return; + } + + console.log(sidebarThreadResponse); } export { taggedCommFarcasterResponder, taggedCommFarcasterInputValidator }; diff --git a/lib/shared/sidebar-utils.js b/lib/shared/sidebar-utils.js --- a/lib/shared/sidebar-utils.js +++ b/lib/shared/sidebar-utils.js @@ -53,6 +53,10 @@ ... }; +function createSidebarThreadName(messageTitle: string): string { + return trimText(messageTitle, 30); +} + function baseCreatePendingSidebar( input: BaseCreatePendingSidebarInput, ): ThreadInfo { @@ -63,7 +67,7 @@ messageTitle, } = input; const { color, type: parentThreadType } = parentThreadInfo; - const threadName = trimText(messageTitle, 30); + const threadName = createSidebarThreadName(messageTitle); const initialMembers = new Map(); @@ -246,4 +250,5 @@ createPendingSidebar, useCanCreateSidebarFromMessage, useSidebarExistsOrCanBeCreated, + createSidebarThreadName, };