Skip to content

Commit ac68b9c

Browse files
committed
Update Sendbird migration script
1 parent 6a34d28 commit ac68b9c

File tree

10 files changed

+327
-109
lines changed

10 files changed

+327
-109
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const appID = "<APP_ID>"; // Add your app ID from the TalkJS dashboard
2+
exports.appID = appID;
3+
const secretKey = "<SECRET_KEY>"; // Add your secret API key from the TalkJS dashboard
4+
exports.secretKey = secretKey;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const fs = require("fs").promises;
2+
const axios = require("axios");
3+
const { secretKey, appID } = require("../config.js");
4+
5+
const instance = axios.create({
6+
baseURL: "https://api.talkjs.com",
7+
headers: {
8+
Authorization: `Bearer ${secretKey}`,
9+
"Content-Type": "application/json",
10+
},
11+
});
12+
13+
async function importChannels(filePath) {
14+
try {
15+
// Read and parse file
16+
const data = await fs.readFile(filePath, "utf8");
17+
console.log(`Read file: ${filePath}`);
18+
19+
const channelsJson = JSON.parse(data);
20+
console.log(
21+
`Parsed JSON data with ${
22+
channelsJson.group_channels?.length || 0
23+
} channels`
24+
);
25+
26+
if (!Array.isArray(channelsJson.group_channels)) {
27+
throw new Error("Invalid data structure: group_channels is not an array");
28+
}
29+
30+
for (let channel of channelsJson.group_channels) {
31+
try {
32+
const channelParticipants = [];
33+
for (let member of channel.members) {
34+
channelParticipants.push(member.user_id);
35+
}
36+
37+
// Create the TalkJS conversation data
38+
const conversation = {
39+
participants: channelParticipants,
40+
};
41+
42+
// Only add subject if channel name exists
43+
if (channel.name) {
44+
conversation.subject = channel.name;
45+
}
46+
47+
await instance.put(
48+
`/v1/${appID}/conversations/${channel.channel_url}`,
49+
conversation
50+
);
51+
console.log(`Imported conversation: ${channel.channel_url}`);
52+
} catch (err) {
53+
console.error(
54+
`Failed to import conversation ${channel.channel_url}:`,
55+
err.response?.data || err.message
56+
);
57+
}
58+
}
59+
} catch (err) {
60+
console.error(`Failed to process channel file ${filePath}:`, err.message);
61+
throw err;
62+
}
63+
}
64+
65+
module.exports = { importChannels };
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
const fs = require("fs").promises;
2+
const axios = require("axios");
3+
const { secretKey, appID } = require("../config.js");
4+
5+
const instance = axios.create({
6+
baseURL: "https://api.talkjs.com",
7+
headers: {
8+
Authorization: `Bearer ${secretKey}`,
9+
"Content-Type": "application/json",
10+
},
11+
});
12+
13+
// Batch size of messages to import in one go
14+
// The TalkJS import endpoint has a limit of 10 messages per request
15+
const batchSize = 10;
16+
17+
async function processMessages(messages) {
18+
const uniqueConversations = [
19+
...new Set(messages.map((item) => item.channel_url)),
20+
];
21+
console.log(`Found ${uniqueConversations.length} unique conversations`);
22+
23+
let conversationJsonArray = [];
24+
let skippedCount = 0;
25+
26+
// Initialize conversation objects
27+
for (let conversation of uniqueConversations) {
28+
let conversationJsonObj = {};
29+
conversationJsonObj[conversation] = [];
30+
conversationJsonArray.push(conversationJsonObj);
31+
}
32+
33+
// Group messages by conversation
34+
for (let conversation of conversationJsonArray) {
35+
for (let message of messages) {
36+
// Skip messages with removed flag
37+
if (message.is_removed === true) {
38+
console.log(`Skipping removed message: ${message.message_id}`);
39+
skippedCount++;
40+
continue;
41+
}
42+
// Skip messages with a non-MESG Sendbird message type
43+
if (message.type !== "MESG") {
44+
console.log(`Skipping non-MESG type message: ${message.message_id}`);
45+
skippedCount++;
46+
continue;
47+
}
48+
49+
if (Object.keys(conversation)[0] === message.channel_url) {
50+
let messageObj = {
51+
text: message.message,
52+
sender: message.user.user_id,
53+
type: "UserMessage",
54+
timestamp: message.created_at,
55+
readBy: [],
56+
};
57+
conversation[message.channel_url].push(messageObj);
58+
}
59+
}
60+
}
61+
62+
console.log(`Skipped ${skippedCount} messages`);
63+
return conversationJsonArray;
64+
}
65+
66+
async function importBatch(conversationId, messages) {
67+
await instance.post(
68+
`/v1/${appID}/import/conversations/${conversationId}/messages`,
69+
messages
70+
);
71+
console.log(
72+
`Imported batch of ${messages.length} messages for conversation: ${conversationId}`
73+
);
74+
}
75+
76+
async function importMessages(filePath) {
77+
try {
78+
// Read and parse file
79+
const data = await fs.readFile(filePath, "utf8");
80+
console.log(`Read file: ${filePath}`);
81+
82+
const messagesJson = JSON.parse(data);
83+
console.log(
84+
`Parsed JSON data with ${messagesJson.messages?.length || 0} messages`
85+
);
86+
87+
if (!Array.isArray(messagesJson.messages)) {
88+
throw new Error("Invalid data structure: messages is not an array");
89+
}
90+
91+
const conversationJsonArray = await processMessages(messagesJson.messages);
92+
93+
// Import messages for each conversation in batches
94+
for (let conversation of conversationJsonArray) {
95+
const conversationId = Object.keys(conversation)[0];
96+
const messages = conversation[conversationId];
97+
98+
console.log(
99+
`Processing ${messages.length} messages for conversation: ${conversationId}`
100+
);
101+
102+
// Split messages into batches
103+
for (let i = 0; i < messages.length; i += batchSize) {
104+
const batch = messages.slice(i, i + batchSize);
105+
try {
106+
await importBatch(conversationId, batch);
107+
} catch (err) {
108+
console.error(
109+
`Failed to import batch for conversation ${conversationId}:`,
110+
err.response?.data || err.message
111+
);
112+
}
113+
}
114+
}
115+
} catch (err) {
116+
console.error(`Failed to process message file ${filePath}:`, err.message);
117+
throw err;
118+
}
119+
}
120+
121+
module.exports = { importMessages };
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const fs = require("fs").promises;
2+
const axios = require("axios");
3+
const { secretKey, appID } = require("../config.js");
4+
5+
const instance = axios.create({
6+
baseURL: "https://api.talkjs.com",
7+
headers: {
8+
Authorization: `Bearer ${secretKey}`,
9+
"Content-Type": "application/json",
10+
},
11+
});
12+
13+
async function importUsers(filePath) {
14+
try {
15+
// Read and parse file
16+
const data = await fs.readFile(filePath, "utf8");
17+
console.log(`Read file: ${filePath}`);
18+
19+
const userData = JSON.parse(data);
20+
console.log(
21+
`Parsed JSON data with ${userData.active_users?.length || 0} users`
22+
);
23+
24+
if (!Array.isArray(userData.active_users)) {
25+
throw new Error("Invalid data structure: active_users is not an array");
26+
}
27+
28+
for (let sendbirdUser of userData.active_users) {
29+
try {
30+
// Build user with required fields
31+
const user = {
32+
id: sendbirdUser.user_id,
33+
name: sendbirdUser.nickname,
34+
};
35+
36+
// Add optional fields if they exist
37+
if (sendbirdUser.metadata?.emailId) {
38+
user.email = [sendbirdUser.metadata.emailId];
39+
}
40+
41+
if (sendbirdUser.profile_url) {
42+
user.photoUrl = sendbirdUser.profile_url;
43+
}
44+
45+
await instance.put(`/v1/${appID}/users/${sendbirdUser.user_id}`, user);
46+
console.log(`Imported user: ${sendbirdUser.user_id}`);
47+
} catch (err) {
48+
console.error(
49+
`Failed to import user ${sendbirdUser.user_id}:`,
50+
err.response?.data || err.message
51+
);
52+
}
53+
}
54+
} catch (err) {
55+
console.error(`Failed to process user file ${filePath}:`, err.message);
56+
throw err;
57+
}
58+
}
59+
60+
module.exports = { importUsers };

rest-api/migrate-data-from-sendbird-to-talkjs/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rest-api/migrate-data-from-sendbird-to-talkjs/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
"name": "migrate-data-from-sendbird-to-talkjs",
33
"version": "1.0.0",
44
"description": "",
5-
"main": "talkjs-migration-users.js",
5+
"main": "runImport.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"start": "node runImport.js"
89
},
910
"author": "",
1011
"license": "ISC",
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const fs = require("fs").promises;
2+
const path = require("path");
3+
const { importUsers } = require("./importers/userImporter");
4+
const { importChannels } = require("./importers/channelImporter");
5+
const { importMessages } = require("./importers/messageImporter");
6+
7+
async function runImport() {
8+
try {
9+
console.log("Starting import process...");
10+
11+
// Import users
12+
const userFiles = await fs.readdir(
13+
path.join(__dirname, "sendbird-data/users")
14+
);
15+
console.log(`\nFound ${userFiles.length} user files to process...`);
16+
17+
for (const filename of userFiles) {
18+
try {
19+
const filePath = path.join(__dirname, "sendbird-data/users", filename);
20+
await importUsers(filePath);
21+
} catch (err) {
22+
console.error(`Failed to import users from ${filename}`);
23+
}
24+
}
25+
26+
// Import channels
27+
const channelFiles = await fs.readdir(
28+
path.join(__dirname, "sendbird-data/channels")
29+
);
30+
console.log(`\nFound ${channelFiles.length} channel files to process...`);
31+
32+
for (const filename of channelFiles) {
33+
try {
34+
const filePath = path.join(
35+
__dirname,
36+
"sendbird-data/channels",
37+
filename
38+
);
39+
await importChannels(filePath);
40+
} catch (err) {
41+
console.error(`Failed to import channels from ${filename}`);
42+
}
43+
}
44+
45+
// Import messages
46+
const messageFiles = await fs.readdir(
47+
path.join(__dirname, "sendbird-data/messages")
48+
);
49+
console.log(`\nFound ${messageFiles.length} message files to process...`);
50+
51+
for (const filename of messageFiles) {
52+
try {
53+
const filePath = path.join(
54+
__dirname,
55+
"sendbird-data/messages",
56+
filename
57+
);
58+
await importMessages(filePath);
59+
} catch (err) {
60+
console.error(`Failed to import messages from ${filename}`);
61+
}
62+
}
63+
} catch (err) {
64+
console.error("Import process failed:", err.message);
65+
process.exit(1);
66+
}
67+
}
68+
69+
runImport().catch((err) => {
70+
console.error("Fatal error during import:", err.message);
71+
process.exit(1);
72+
});

rest-api/migrate-data-from-sendbird-to-talkjs/talkjs-migration-conversations.js

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)