Created
December 1, 2021 12:33
-
-
Save charisTheo/7f494109392ddab97a7bb5adb0f06a5e to your computer and use it in GitHub Desktop.
This script migrates all messages and users from a Sendbird supergroup channel to a group channel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This script migrates all messages and users | |
* from a Sendbird supergroup channel to a group channel | |
* by creating a new group channel and deleting the old supergroup channel. | |
* | |
* Note: make sure you have chat history enabled for newly joined members | |
* Enable it by selecting an App from the Dashboard and toggle the checkbox under | |
* Settings > Chat > Group Channels > Chat history | |
* | |
* To use this script, you need to change APP_ID and API_TOKEN below | |
* You should already have a supergroup channel created and use its URL in SUPERGROUP_CHANNEL_URL | |
*/ | |
import fetch from 'node-fetch'; | |
const APP_ID = "<YOUR APP ID HERE>"; | |
const API_TOKEN = "<YOUR API TOKEN HERE>"; | |
const SUPERGROUP_CHANNEL_URL = "breathing_channel"; | |
/** | |
* Docs: https://sendbird.com/docs/chat/v3/platform-api/guides/group-channel#2-view-a-channel | |
* | |
* @param {String} channelUrl | |
* @returns {Object | null} - channel object or null if error | |
*/ | |
async function getSupergroupChannel(channelUrl) { | |
const requestUrl = `https://api-${APP_ID}.sendbird.com/v3/group_channels/${channelUrl}?show_member=true`; | |
try { | |
const response = await fetch(requestUrl, { | |
headers: { | |
'Api-Token': API_TOKEN | |
} | |
}) | |
if (response.status !== 200) { | |
console.log(`\n❌ getSupergroupChannel - Failed request: ${response.status} - ${response.statusText}`) | |
return null | |
} | |
const channel = await response.json() | |
console.log(`\n✅ Retrieved a supergroup channel with URL: ${channel.channel_url}.`) | |
return channel | |
} catch (error) { | |
console.error(`\n❌ getSupergroupChannel Error: ${error}`) | |
return null | |
} | |
} | |
/** | |
* Adds a delay before creating the new channel | |
* which will be using the same URL as the previously deleted channel | |
* Docs: https://sendbird.com/docs/chat/v3/platform-api/guides/group-channel#2-create-a-channel | |
* | |
* @param {Object} oldChannel | |
* @returns {Object | null} - new channel object or null if error | |
*/ | |
function createGroupChannel(oldChannel) { | |
return new Promise(resolve => { | |
const requestUrl = `https://api-${APP_ID}.sendbird.com/v3/group_channels`; | |
setTimeout(async () => { | |
try { | |
const invitation_status = oldChannel.members.reduce((acc, cur) => { | |
acc[cur.user_id] = 'joined' | |
return acc | |
}, {}) | |
const response = await fetch(requestUrl, { | |
method: 'POST', | |
headers: { | |
'Api-Token': API_TOKEN, | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
name: oldChannel.name, | |
cover_url: oldChannel.cover_url, | |
channel_url: oldChannel.channel_url, | |
user_ids: oldChannel.members.map(member => member.user_id), | |
invitation_status, | |
// Additionally you can set here the hidden_status to 'hidden_allow_auto_unhide' | |
// and only show the channel in user's channel list after full migration is complete | |
is_super: false, | |
is_distinct: true | |
}) | |
}) | |
if (response.status !== 200) { | |
console.log(`\n❌ createGroupChannel - Failed request: ${response.status} - ${response.statusText}`) | |
resolve(null) | |
return | |
} | |
const channel = await response.json() | |
console.log(`\n🆕 Created a new group channel with URL: ${channel.channel_url}.`) | |
resolve(channel) | |
} | |
catch (error) { | |
console.error(`\n❌ createGroupChannel Error: ${error}`) | |
resolve(null) | |
} | |
}, 2000) | |
}) | |
} | |
/** | |
* Extra: Use include_reactions=true in requestUrl to migrate reactions | |
* Docs: https://sendbird.com/docs/chat/v3/platform-api/guides/messages#2-list-messages | |
* | |
* @param {Object} channel | |
* @returns {Array<Object> | null} - array of message objects or null if error | |
*/ | |
async function getGroupChannelMessages(channel) { | |
const requestUrl = `https://api-${APP_ID}.sendbird.com/v3/group_channels/${channel.channel_url}/messages?message_ts=${channel.last_message.created_at}&prev_limit=200`; | |
try { | |
const response = await fetch(requestUrl, { | |
headers: { | |
'Api-Token': API_TOKEN | |
} | |
}) | |
if (response.status !== 200) { | |
console.log(`\n❌ getGroupChannelMessages - Failed request: ${response.status} - ${response.statusText}`) | |
return null | |
} | |
const { messages } = await response.json() | |
console.log(`\n💬 Retrieved ${messages.length} messages.`) | |
return messages | |
} | |
catch (error) { | |
console.error(`\n❌ getGroupChannelMessages Error: ${error}`) | |
return null | |
} | |
} | |
/** | |
* Using Migration API to migrate messages | |
* Note: The maximum number of migrated messages per call is 100 | |
* Docs: https://sendbird.com/docs/chat/v3/platform-api/guides/migration | |
* | |
* @param {String} channelUrl | |
* @returns {Boolean} - true if success, false if error | |
*/ | |
async function migrateMessagesToChannel(messages, channelUrl) { | |
if (!messages || !messages?.length) { | |
return false | |
} | |
const requestUrl = `https://api-${APP_ID}.sendbird.com/v3/migration/${channelUrl}`; | |
// TODO: do separate calls for admin and file messages | |
// Admin messages: https://sendbird.com/docs/chat/v3/platform-api/guides/migration#4-list-of-properties-for-admin-message | |
// const adminMessages = messages.filter(message => message.message_type === "ADMM") | |
// File messages: https://sendbird.com/docs/chat/v3/platform-api/guides/migration#4-list-of-properties-for-file-message | |
// const fileMessages = messages.filter(message => message.message_type === "FILE") | |
const userMessages = messages.reduce((acc, cur) => { | |
if (cur.type === "MESG") { | |
acc.push({ | |
user_id: cur.user.user_id, | |
message_type: cur.type, | |
message: cur.message, | |
timestamp: cur.created_at, | |
}) | |
} | |
return acc | |
}, []) | |
try { | |
const response = await fetch(requestUrl, { | |
method: 'POST', | |
headers: { | |
'Api-Token': API_TOKEN, | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
messages: userMessages, | |
update_read_ts: false, | |
rewind_read_ts: false | |
}) | |
}) | |
if (response.status !== 200) { | |
console.log(`\n❌ migrateMessagesToChannel - Failed request: ${response.status} - ${response.statusText}`) | |
return false | |
} | |
console.log(`\n💬 Migrated ${messages.length} messages.`) | |
return true | |
} | |
catch (error) { | |
console.error(`\n❌ migrateMessages Error: ${error}`) | |
return false | |
} | |
} | |
/** | |
* Docs: https://sendbird.com/docs/chat/v3/platform-api/guides/group-channel#2-delete-a-channel | |
* | |
* @param {String} channelUrl | |
* @returns {Boolean} - true if success, false if error | |
*/ | |
async function deletePreviousChannel(channelUrl) { | |
const requestUrl = `https://api-${APP_ID}.sendbird.com/v3/group_channels/${channelUrl}`; | |
try { | |
const response = await fetch(requestUrl, { | |
method: 'DELETE', | |
headers: { | |
'Api-Token': API_TOKEN, | |
'Content-Type': 'application/json' | |
} | |
}) | |
if (response.status !== 200) { | |
console.log(`\n❌ deletePreviousChannel - Failed request: ${response.status} - ${response.statusText}`) | |
return false | |
} | |
console.log(`\n🗑 Deleted channel with URL: ${channelUrl}`) | |
return true | |
} catch (error) { | |
console.error(`\n❌ deletePreviousChannel - Error: ${error}`) | |
return false | |
} | |
} | |
(async function() { | |
const supergroupChannel = await getSupergroupChannel(SUPERGROUP_CHANNEL_URL) | |
if (!supergroupChannel) { | |
return | |
} | |
const messages = await getGroupChannelMessages(supergroupChannel) | |
const deletedPrevious = await deletePreviousChannel(SUPERGROUP_CHANNEL_URL) | |
if (!deletedPrevious) { | |
return | |
} | |
const groupChannel = await createGroupChannel(supergroupChannel) | |
if (!groupChannel) { | |
return | |
} | |
const migrated = await migrateMessagesToChannel(messages, groupChannel.channel_url) | |
if (!migrated) { | |
console.log(`\n❌ Migration failed`) | |
return | |
} | |
console.log(`\n✅ Migration completed`) | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment