Export all comments into new entity - filtered by Mention

I created two scripts, which work independently, you can choose which one best fits your case.

Script A (without Mention Filter)

Focuses on gathering comments from a specified channel, formatting them for readability, and then creating a new thread entity that compiles these comments in a structured manner.

Use Cases for Script A:

  • Summarizing feedback from team discussions or customer feedback channels.
  • Compiling notes or discussions from meetings into a single, accessible document.
  • Archiving discussions for future reference or for newcomers to quickly catch up.

Script B (with Mention Filter)

Extends the functionality of Script A by adding a filtering mechanism based on Mention links within comments. It fetches comments from a channel, filters them based on specified Mention entity, formats the filtered comments, and creates a new thread entity containing only the comments that match the filtering criteria.

Use Cases for Script B:

  • Creating focused summaries of discussions that pertain only to specific topics, identified by tags.
  • Filtering customer feedback or team discussions to isolate comments relevant to particular features, issues, or areas of interest.
  • Enhancing project management by automatically organizing task-related discussions into dedicated threads for better clarity and actionability.

Script A: Without Mention Filter

const fibery = context.getService('fibery');
const threadsDb = 'Threads/Channel'; // Database for Channel
const threadDb = 'Threads/Thread'; // Database for Thread
const fiberyBaseURL = 'https://cce.fibery.io'; // Base URL for your Fibery instance

async function formatComment(comment, indentLevel = 0, channelPublicId) {
    const commentEntity = await fibery.getEntityById('comments/comment', comment['Id'], ['Author', 'fibery/creation-date', 'comments/parent', 'fibery/public-id']);
    const authorName = commentEntity['Author']['Name'] || "Unknown Author";
    const commentContent = await fibery.getDocumentContent(comment['Document Secret'], 'md');
    const creationDate = new Date(commentEntity['fibery/creation-date']);
    const formattedDate = `${creationDate.getFullYear()}-${String(creationDate.getMonth() + 1).padStart(2, '0')}-${String(creationDate.getDate()).padStart(2, '0')}, ${String(creationDate.getHours()).padStart(2, '0')}:${String(creationDate.getMinutes()).padStart(2, '0')}`;
    const commentPublicId = commentEntity['fibery/public-id'];
    const headerLink = `[**${authorName}** (${formattedDate})](${fiberyBaseURL}/Threads/Channel/${channelPublicId}/comment=${commentPublicId})`;
    const indentation = "> ".repeat(indentLevel);
    return `${indentation}${headerLink}:\n${indentation}${commentContent.replace(/\n/g, `\n${indentation}`)}\n\n`;
}

async function createThreadWithComments(entityId) {
    // Fetching the Channel entity to obtain its name and public ID
    const channelEntity = await fibery.getEntityById(threadsDb, entityId, ['Name', 'fibery/public-id']);
    const channelName = channelEntity['Name'];
    const channelPublicId = channelEntity['fibery/public-id'];

    const entityWithComments = await fibery.getEntityById(threadsDb, entityId, ['Comments']);
    let content = "";

    if (entityWithComments.Comments && entityWithComments.Comments.length > 0) {
        const topLevelComments = [];
        const repliesMap = {};

        for (const comment of entityWithComments.Comments) {
            const commentEntity = await fibery.getEntityById('comments/comment', comment['Id'], ['comments/parent', 'fibery/public-id']);
            const parentId = commentEntity['comments/parent'] ? commentEntity['comments/parent']['Id'] : null;

            if (parentId) {
                if (!repliesMap[parentId]) {
                    repliesMap[parentId] = [];
                }
                repliesMap[parentId].push(comment);
            } else {
                topLevelComments.push(comment);
            }
        }

        for (const comment of topLevelComments) {
            content += await formatComment(comment, 0, channelPublicId);
            const replies = repliesMap[comment['Id']];
            if (replies && replies.length > 0) {
                for (const reply of replies) {
                    content += await formatComment(reply, 1, channelPublicId);
                }
            }
        }
    } else {
        console.log(`No comments found for entity ${entityId}.`);
        return;
    }

    const creationTimestamp = new Date();
    const formattedTimestamp = `${creationTimestamp.getFullYear()}-${String(creationTimestamp.getMonth() + 1).padStart(2, '0')}-${String(creationTimestamp.getDate()).padStart(2, '0')} ${String(creationTimestamp.getHours()).padStart(2, '0')}:${String(creationTimestamp.getMinutes()).padStart(2, '0')}`;
    const newThreadData = { 'Name': `${channelName} Comments (${formattedTimestamp})`, 'Channel': entityId };
    const newThread = await fibery.createEntity(threadDb, newThreadData);

    if (newThread['Description'] && newThread['Description'].Secret) {
        await fibery.setDocumentContent(newThread['Description'].Secret, content, 'md');
        console.log(`New Thread entity created and description updated with comments for Channel entity ${entityId}. Thread ID: ${newThread.id}.`);
    } else {
        console.log(`Failed to update Description for the new Thread entity linked to Channel entity ${entityId}.`);
    }
}

async function processChannelComments() {
    try {
        if (!args.currentEntities || args.currentEntities.length === 0) {
            throw new Error('No current entity found.');
        }

        for (const currentEntity of args.currentEntities) {
            await createThreadWithComments(currentEntity.id);
        }
    } catch (error) {
        console.error(`Error processing comments for the entity: ${error.message}`, error);
    }
}

await processChannelComments();

Script B: With Mention Filter

const fibery = context.getService('fibery');
const threadsDb = 'Threads/Channel'; // Adjust according to your actual Channel database
const threadDb = 'Threads/Thread'; // Adjust to your Thread database
const fiberyBaseURL = 'https://cce.fibery.io'; // Base URL for your Fibery instance

async function fetchChannelTags(channelId) {
    const channelEntity = await fibery.getEntityById(threadsDb, channelId, ['Tags']);
    return channelEntity.Tags ? channelEntity.Tags : [];
}

async function fetchCommentsForChannel(channelId) {
    const entity = await fibery.getEntityById(threadsDb, channelId, ['Comments']);
    return entity.Comments ? entity.Comments : [];
}

async function filterCommentsByTagUuids(comments, tagUuids) {
    const matchingComments = [];
    for (const comment of comments) {
        const commentContent = await fibery.getDocumentContent(comment['Document Secret'], 'md');
        if (tagUuids.some(tagUuid => commentContent.includes(tagUuid))) {
            matchingComments.push(comment);
        }
    }
    return matchingComments;
}

async function formatComment(comment, channelPublicId) {
    const commentEntity = await fibery.getEntityById('comments/comment', comment['Id'], ['Author', 'fibery/creation-date', 'fibery/public-id']);
    const authorName = commentEntity['Author']['Name'] || "Unknown Author";
    const creationDate = new Date(commentEntity['fibery/creation-date']);
    const formattedDate = `${creationDate.getFullYear()}-${String(creationDate.getMonth() + 1).padStart(2, '0')}-${String(creationDate.getDate()).padStart(2, '0')} ${String(creationDate.getHours()).padStart(2, '0')}:${String(creationDate.getMinutes()).padStart(2, '0')}`;
    const commentPublicId = commentEntity['fibery/public-id'];
    const headerLink = `[**${authorName}** (${formattedDate})](${fiberyBaseURL}/Threads/Channel/${channelPublicId}/comment=${commentPublicId})`;
    const commentContent = await fibery.getDocumentContent(comment['Document Secret'], 'md');
    return `${headerLink}:\n${commentContent}\n\n`;
}

async function createThreadWithComments(entityName, comments, channelPublicId) {
    const newThreadData = {
        'Name': entityName,
        'Channel': channelPublicId,
    };
    const newThread = await fibery.createEntity(threadDb, newThreadData);

    let descriptionContent = "Filtered Comments:\n\n";
    for (const comment of comments) {
        descriptionContent += await formatComment(comment, channelPublicId);
    }

    await fibery.setDocumentContent(newThread['Description'].Secret, descriptionContent, 'md');
    return `New Thread created with ID: ${newThread.id}, linked to Channel ${channelPublicId}, containing ${comments.length} filtered comments.`;
}

async function processChannelComments() {
    try {
        if (!args.currentEntities || args.currentEntities.length === 0) {
            return 'No current entity found to process.';
        }
        let messages = [];
        for (const currentEntity of args.currentEntities) {
            const channelId = currentEntity.id;
            const tags = await fetchChannelTags(channelId);
            const tagUuids = tags.map(tag => tag['Id']);
            const tagNames = tags.map(tag => `[${tag['Name']}]`).join(' ');
            const comments = await fetchCommentsForChannel(channelId);
            const matchingComments = await filterCommentsByTagUuids(comments, tagUuids);

            if (matchingComments.length > 0) {
                const creationTimestamp = new Date();
                const formattedTimestamp = `${creationTimestamp.getFullYear()}-${String(creationTimestamp.getMonth() + 1).padStart(2, '0')}-${String(creationTimestamp.getDate()).padStart(2, '0')} ${String(creationTimestamp.getHours()).padStart(2, '0')}:${String(creationTimestamp.getMinutes()).padStart(2, '0')}`;
                const entityName = `Filtered Comments with ${tagNames} ${formattedTimestamp}`;
                const message = await createThreadWithComments(entityName, matchingComments, channelId);
                messages.push(message);
            } else {
                messages.push(`No matching comments found for the selected tags in Channel ${channelId}.`);
            }
        }
        return messages.join("\n");
    } catch (error) {
        throw new Error(`Error processing comments for the channel: ${error.message}`);
    }
}

return await processChannelComments();

Explanation

Script A

  1. Initialize Fibery service and database names.
  2. Define formatComment function to fetch comment details, format content, and construct clickable link headers.
  3. Define createThreadWithComments function to fetch channel details, organize comments into a structured format, and create a new thread with formatted comments.
  4. Fetch channel’s public ID for URL construction.
  5. Iterate through comments to distinguish top-level comments from replies, applying formatting recursively.
  6. Construct a new thread entity with a timestamped name and link it to the channel.
  7. Update the new thread’s description with the formatted comments.
  8. Define processChannelComments as the main function to process each provided channel entity, using the above functions.
  9. Execute processChannelComments, orchestrating the script’s operations.
  10. Log success or failure messages based on the outcome of operations.

Script B:

  1. Initialize Fibery service and database constants.
  2. Define fetchChannelTags to retrieve tags associated with a given channel.
  3. Define fetchCommentsForChannel to fetch all comments linked to a channel.
  4. Define filterCommentsByTagUuids to filter comments based on content containing any tag UUIDs.
  5. Define formatComment to format comment details and create clickable link headers.
  6. Define createThreadWithComments to compile filtered comments into a description and create a new thread entity linked to the channel.
  7. In createThreadWithComments, use the channel’s public ID for URL constructions within comments and the internal UUID for entity linking.
  8. Define processChannelComments as the main function to orchestrate fetching tags, comments, filtering by tags, and thread creation.
  9. Execute the filtering logic; if no comments match the tags, prepare a message indicating no matching comments were found.
  10. If matching comments are found, proceed with thread creation and compile a success message detailing the action taken.
  11. Adjust processChannelComments to return a custom message to the user based on the outcome, including success notifications or error messages if applicable.
  12. Execute processChannelComments and return its output as the script’s final action, displaying the outcome message to the user.
3 Likes