Discussions

Ask a Question
Back to all

Userscript to help you export your groups and chats

Hi all, I'd like to share a little userscript I made this morning. I found it really frustrating when having to change filters for multiple groups as loading them one after the other on the Connecteam interface becomes cumbersome as we have over 1000 groups. This userscript adds buttons to the connecteam header so you can export CSVs for groups and chats. I hope somebody finds it helpful.

// ==UserScript==
// @name         Connecteam Group and Chat Exporter
// @namespace    https://rlcg.org.au
// @version      2025-08-13
// @description  Gives the user quick buttons to export groups and chats information to CSV
// @author       Jacob Mulquin
// @match        https://app.connecteam.com/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==

(function () {
	'use strict';

    function waitForElement(selector) {
        return new Promise(resolve => {
            if (document.querySelector(selector)) {
                return resolve(document.querySelector(selector));
            }

            const observer = new MutationObserver(() => {
                if (document.querySelector(selector)) {
                    resolve(document.querySelector(selector));
                    observer.disconnect();
                }
            });

            observer.observe(document.body, {
                subtree: true,
                childList: true,
            });
        });
    }

	function download(content, fileName, mimeType) {
		const a = document.createElement('a');
		mimeType = mimeType || 'application/octet-stream';
		const blob = new Blob([content], { type: mimeType });
		a.href = URL.createObjectURL(blob);
		a.download = fileName;
		document.body.appendChild(a);
		a.click();
		document.body.removeChild(a);
		URL.revokeObjectURL(a.href);
	}

    waitForElement('div.app-header-right-side').then(element => {
        const header = document.querySelector('div.app-header-right-side');

        header.insertAdjacentHTML('afterBegin', '<div class="ct-loader" style="display: none; width: 34px; height: 34px; color: var(--ct-gray-6);"><svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.95109 7.26777C2.36913 6.03626 3.06417 4.91714 3.98266 3.99653C3.98649 3.9927 3.99269 3.9927 3.99652 3.99652V3.99652C4.00035 4.00035 4.00034 4.00656 3.99652 4.01039C3.08016 4.92887 2.38673 6.0454 1.96965 7.27407C1.55153 8.50582 1.42236 9.81726 1.59215 11.1069C1.76194 12.3966 2.22613 13.6299 2.94881 14.7115C3.67149 15.793 4.63328 16.6938 5.7598 17.3442C6.88632 17.9946 8.14735 18.3772 9.44536 18.4622C10.7434 18.5473 12.0435 18.3327 13.2453 17.8349C14.4471 17.3371 15.5182 16.5695 16.3759 15.5915C17.2314 14.616 17.8516 13.4572 18.1889 12.2044C18.1903 12.1991 18.1957 12.196 18.2009 12.1974V12.1974C18.2061 12.1988 18.2092 12.2042 18.2078 12.2094C17.8698 13.4652 17.2481 14.6266 16.3906 15.6044C15.531 16.5847 14.4574 17.354 13.2528 17.853C12.0483 18.3519 10.7451 18.5671 9.44407 18.4818C8.14307 18.3965 6.87912 18.0131 5.75 17.3612C4.62088 16.7093 3.65686 15.8064 2.93251 14.7223C2.20816 13.6383 1.7429 12.4021 1.57272 11.1095C1.40254 9.81683 1.532 8.50237 1.95109 7.26777Z" stroke="currentColor" stroke-width="3"></path></svg></div><button type="button" id="export_groups" class="ct-button text big has-prefix has-text" tabindex="0" style="--primary-idle: var(--ct-blue-6); --primary-hover: var(--ct-blue-5); --primary-active: var(--ct-blue-7); --accent: var(--ct-blue-6); --highlighted-bg: var(--ct-blue-1); --highlighted-color: var(--ct-blue-6); --highlighted-border: var(--ct-blue-3); --highlighted-hover-bg: var(--ct-blue-2); --highlighted-hover-color: var(--ct-blue-6); --highlighted-hover-border: var(--ct-blue-3); --highlighted-active-bg: var(--ct-blue-3); --highlighted-active-color: var(--ct-blue-7); --highlighted-active-border: var(--ct-blue-3);"><div class="ct-button-content"><span>Export Groups</span></div></button><div class="app-header-divider"></div><button type="button" id="export_chats" class="ct-button text big has-prefix has-text" tabindex="0" style="--primary-idle: var(--ct-blue-6); --primary-hover: var(--ct-blue-5); --primary-active: var(--ct-blue-7); --accent: var(--ct-blue-6); --highlighted-bg: var(--ct-blue-1); --highlighted-color: var(--ct-blue-6); --highlighted-border: var(--ct-blue-3); --highlighted-hover-bg: var(--ct-blue-2); --highlighted-hover-color: var(--ct-blue-6); --highlighted-hover-border: var(--ct-blue-3); --highlighted-active-bg: var(--ct-blue-3); --highlighted-active-color: var(--ct-blue-7); --highlighted-active-border: var(--ct-blue-3);"><div class="ct-button-content"><span>Export Chats</span></div></button>');
        const groups_button = document.getElementById('export_groups');
        groups_button.addEventListener('click', function () {
            header.querySelector('.ct-loader').style.display = 'block';
            fetch('https://app.connecteam.com/api/Creator/GroupSegment/?disableAuthorizationFilter=1')
                .then(response => response.json())
                .then(data => {
                header.querySelector('.ct-loader').style.display = 'none';
                let csv = 'Segment ID,Segment Name,Group ID,Group ID Number,Group Name\n';
                data.data.forEach(segment => {
                    const segId = segment.id;
                    const segName = segment.name.replace(/"/g, '""'); // Escape double quotes
                    segment.groups.forEach(group => {
                        const groupId = group.id;
                        const groupIdNum = group.idNumber;
                        const groupName = group.name.replace(/"/g, '""'); // Escape double quotes
                        csv += `"${segId}","${segName}","${groupId}","${groupIdNum}","${groupName}"\n`;
                    });
                });
                download(csv, 'connecteam_groups.csv', 'text/csv');
            })
            .catch(error => console.error('Error fetching segments:', error));
        });

        const chat_button = document.getElementById('export_chats');
        chat_button.addEventListener('click', function () {
            header.querySelector('.ct-loader').style.display = 'block';
            fetch('https://app.connecteam.com/api/Chat/Conversations/Manage/')
                .then(response => response.json())
                .then(data => {
                header.querySelector('.ct-loader').style.display = 'none';
                let csv = 'ID,Type,Association,Title,Description,Associated Groups,Member Count,Message Count\n';
                data.data.conversations.forEach(conversation => {
                    const id = conversation.id;
                    const type = conversation.type;
                    const association = conversation.association;
                    const title = conversation.title ? conversation.title.replace(/"/g, '""') : '';
                    let description = '';
                    if (conversation.description && conversation.description.length > 0 && conversation.description[0].html) {
                        description = conversation.description[0].html.replace(/"/g, '""');
                    }
                    let associatedGroups = 0;
                    if (data.data.assignByConversationId && data.data.assignByConversationId[id] && data.data.assignByConversationId[id].assignedGroupIds) {
                        associatedGroups = data.data.assignByConversationId[id].assignedGroupIds.length;
                    }
                    let memberCount = 0;
                    if (data.data.membersByConversationId && data.data.membersByConversationId[id]) {
                        memberCount = data.data.membersByConversationId[id].length;
                    }
                    let messageCount = 0;
                    if (data.data.messageByConversationId && data.data.messageByConversationId[id]) {
                        messageCount = data.data.messageByConversationId[id];
                    }
                    csv += `"${id}","${type}","${association}","${title}","${description}",${associatedGroups},${memberCount},${messageCount}\n`;
                });
                download(csv, 'chats.csv', 'text/csv');
            })
            .catch(error => console.error('Error fetching chats:', error));
        });
    });
})();