# Export all comments into new entity - filtered by Mention

**URL:** <https://community.fibery.io/t/export-all-comments-into-new-entity-filtered-by-mention/5961>\
**Category:** Fibery Showcase\
**Created:** [March 11, 2024, 11:23am UTC](https://community.fibery.io/t/export-all-comments-into-new-entity-filtered-by-mention/5961 "2024-03-11T11:23:43Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Yuri\_BC](https://sea2.discourse-cdn.com/flex020/user_avatar/community.fibery.io/yuri_bc/32/8803_2.png) [@Yuri\_BC](https://community.fibery.io/u/Yuri_BC)\
**Post date:** [March 11, 2024, 11:23am UTC](https://community.fibery.io/t/export-all-comments-into-new-entity-filtered-by-mention/5961/1 "2024-03-11T11:23:43Z")

</div>

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.

[https://i.imgur.com/jFjJFT3.mp4](https://i.imgur.com/jFjJFT3.mp4)

## Script A: Without Mention Filter

```auto
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

```auto
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.
