Skip to content

Instantly share code, notes, and snippets.

@kearlsaint
Last active October 27, 2019 12:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kearlsaint/053f96baafba3e1bcd9fec1e45f34c95 to your computer and use it in GitHub Desktop.
Save kearlsaint/053f96baafba3e1bcd9fec1e45f34c95 to your computer and use it in GitHub Desktop.
Snippet of code to create a full XML backup of messages from Pulse Messenger
/*!
*
* Snippet of code to create a full XML backup of messages from Pulse Messenger
* https://github.com/klinker-apps/messenger-desktop
*
* This code utilizes the webapp's core functions.
*
* Instructions
* 1. Log into https://messenger.klinkerapps.com
* 2. Wait for the UI to load your conversations
* 3. Open Developer Console
* 4. Paste the following code
* 5. Wait for your data to download(check Network tab)
* 6. Download SMSBackupRestore from PlayStore (https://play.google.com/store/apps/details?id=com.riteshsahu.SMSBackupRestore)
* 7. Put backup XML file to SD Card
* 8. Restore using SMSBackupRestore
*
* profit 5 cents
*
* Changelog:
* 06 MAY 2018: Changed to multi-query per conversation thread to reduce server load & support slow connections
* --> Set custom limit per query @ line 150, default is 20
* Added a bit of log output to console
*
**/
$(function() {
String.prototype.toHtmlEntities = function() {
// https://stackoverflow.com/a/4237934
// https://stackoverflow.com/a/27020300
// https://stackoverflow.com/a/32712035
var str = this;
if ($.trim(str) != "") {
str = str.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
str = str.replace(/&(?!(amp;)|(lt;)|(gt;)|(quot;)|(#39;)|(apos;))/g, "&amp;");
str = str.replace(/([^\\])((\\\\)*)\\(?![\\/{])/g, "$1\\\\$2"); //replaces odd backslash(\\) with even.
}
return str.replace(/[^\u0009|\u0020-\uD7FF|\uE000-\uFFFD|\uDBFFDFFF-\u10FFFF]/gim, function(s) {
return "&#" + s.charCodeAt(0) + ";";
});
};
// modified version for xml export
// only decrypts .data
// does not convert emojis to web emojis
// converts non-xml-safe chars
function decryptMessages_forExport(data) {
for (var i = data.length - 1; i >= 0; i--) {
try {
data[i].data = entityEncode(decrypt(data[i].data));
data[i].data = data[i].data.toHtmlEntities();
} catch (err) {
data[i].data = "";
}
}
// remove duplicate messages
for (var i = 0; i < data.length; i++) {
var currentId = data[i].device_id;
for (var j = i + 1; j < data.length; j++) {
if (data[j].device_id == currentId) {
data.splice(j, 1);
j--;
}
}
}
return data;
}
function export_to_xml(data) {
/*
* data needed for SMSBackupRestore
* protocol="0"
* address=
* date=
* type=1(received)/2(sent)
* subject="null"
* body=
* toa="null"
* sc_toa="null"
* service_center=""
* read="1"
* status="-1"
* locked="0"
* date_sent=
* readable_date="Jun 4, 2017 23:40:11"
* contact_name=
*/
// xml headers
var xml_string = "";
xml_string += "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
xml_string += '<?xml-stylesheet type="text/xsl" href="sms.xsl"?>\n';
var msg_string = "";
var msg_count = 0;
// loop through each conversation thread
for(var i=0, d, m, n, f; i < data.length; i++) {
d = data[i];
f = d.name;
n = d.number;
t = d.messages;
// loop through each message
for(var j=0, m; j<t.length; j++) {
m = t[j];
msg_string += '<sms';
msg_string += ' protocol="0"';
msg_string += ' address="' + n + '"';
msg_string += ' date="' + m.timestamp + '"';
msg_string += ' type="' + (m.message_type+1) + '"';
msg_string += ' subject="null"';
msg_string += ' body="' + m.data + '"';
msg_string += ' toa="null"';
msg_string += ' sc_toa="null"';
msg_string += ' service_center=""';
msg_string += ' read="1"';
msg_string += ' status="-1"';
msg_string += ' locked="0"';
msg_string += ' date_sent="' + m.timestamp + '"';
msg_string += ' readable_date=""';
msg_string += ' contact_name="' + n + '"';
msg_string += '/>\n';
}
msg_count += t.length;
}
xml_string += '<smses count="' + msg_count + '" backup_date="' + new Date().getTime() + '">\n';
xml_string += msg_string;
xml_string += '</smses>';
// export to sms
var download = document.createElement("a");
download.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(xml_string));
download.setAttribute('download', 'backup-' + new Date().getTime() + '.xml');
$("body").append(download);
download.click();
}
url="https://api.messenger.klinkerapps.com/api/v1/conversations/index_unarchived?account_id=" + accountId;
var messages=[];
var convo_list;
var fetched=0;
$.get(url).done(function(data) {
convo_list = decryptConversations(data);
console.log("Fetching " + convo_list.length + " conversation threads...");
for(var i=0; i<convo_list.length; i++) {
(function(q, z) { // save val of i to q
var t = "https://api.messenger.klinkerapps.com/api/v1/messages?web=true&account_id=" + accountId + "&conversation_id=" + q.device_id + "";
var l = 20, o = 0; // limit, offset
var ret = function() {
t += "&limit=" + l + "&offset=" + o;
$.get(t).done(function(data) {
if(data.length <= 0) {
// no more messages
// mark whole thread as fetched
console.log(" --> fetched thread #" + z);
fetched++;
if(fetched >= convo_list.length) {
console.log("Exporting to XML, please check download progress.");
export_to_xml(messages);
}
} else {
// decrypt and save messages
messages.push({
number: q.phone_numbers,
name: q.title,
messages: decryptMessages_forExport(data)
});
// get next offset
o += l;
setTimeout(ret, 200);
}
});
};
console.log(" --> fetching thread #" + z);
ret();
})(convo_list[i], i + 1);
}
});
});
@klinker24
Copy link

You should probably paginate the messages query. That /messages route will accept a limit and offset query parameter. I recommend a limit of 500, or less, messages per query, just to keep the load a bit lower. The endpoint will time out after 30 seconds.

@kearlsaint
Copy link
Author

Modified the code to paginate the query per thread and hopefully reduce the load. Thanks for the suggestion 👍

@rubaboo
Copy link

rubaboo commented Mar 17, 2019

Getting an error
}' is not a valid selector

@aidenmadaffri
Copy link

aidenmadaffri commented Oct 27, 2019

Does this snippet no longer work? Chrome just says "undefined" when ran in the console.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment