发表于 2020-9-8 20:34

申请会员ID: netelfz【申请通过】

1、申 请 I D:netelfz
2、个人邮箱:topskill@gmail.com
3、原创技术文章:本人原创,从MIUI系统导出短信


本人最近没太多接触破解等相关知识,主要精力都在开发上,由于平常的作品都是商业软件,也不好贴出来展示,所以这里分享一个最近写的实用小工具,无论申请通过与否,有需要的同学都可以根据需要用它。


由于本人多年习惯,换手机的时候所有的短信都会跟随新机转移,将来有事情可以翻查。之前从小米 MIX2 换到华为手机,系统也从 MIUI换到 EMUI。然后用短信备份软件导出短信时,发现所有的通知类短信全部无法导出,只能导出私人短信。百般折腾发现是权限限制,试用了各种短信备份软件都无法正常读取,不禁为粗粮对用户的保(bang)护(jia)点了个👍。由于手机要给老人用,不打算root就搁置了。
后来发现如果开启了云备份,在网上会有一份短信记录,于是我写了个脚本从小米的云平台导出所有短信,转换格式并成功导入华为手机。

环境准备:

[*]要导入的手机安装 SMS Backup & Restore 软件,其他备份软件应该也可以,需要改转换脚本
[*]要导出的小米手机,确保已经将短信备份在云上
[*]电脑安装 Chrome 浏览器,并安装 油猴插件 Tampermonkey,其他浏览器未作测试
[*]电脑上安装 Python3 环境


SMS Backup & Restore 软件图标如下,在 Play商店下载,可能需要一些手段来下载。



迁移步骤:

首先在 Tampermonkey 里新增一个脚本,删掉默认内容并粘贴下面的脚本,按 Ctrl+S 保存。

// ==UserScript==
// @name         小米云短信备份器
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description自动备份小米手机短信,经过处理后可导入其他手机
// @author       You
// @match      https://i.mi.com/v1*
// @grant      none
// @require      https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js
// ==/UserScript==

(function() {
    'use strict';
    let floatLayer = window.document.createElement('div');
    floatLayer.style = "z-index:100; position: -webkit-sticky; position: sticky; left:10px; top:10px; background-color:white";
    var formHtml = '';
    formHtml += '<div style="padding:0 0 10px 10px;"><table border=1 cellpadding=2 cellspacing=2>'
    formHtml += '<tr><td>UserID:</td><td><input readonly size=30 id="user_id"></td></tr>';
    formHtml += '<tr><td>threads:</td><td><input readonly size=30 id="threads"></td></tr>';
    formHtml += '<tr><td rowspan=2>Current:</td><td><input readonly size=120 id="current_title"></td></tr>';
    formHtml += '<tr><td><input readonly size=100 id="current_step"></td></tr>';
    formHtml += '</table></div>';
    formHtml += '<div id="status_line" style="color:red; font-size: 150%; font-weight:bold;padding:0 0 10px 10px;"> </div>';
    formHtml += '<div style="padding:0 0 10px 10px"><button id="start_button">Start</button><button id="export_button" disabled style="display:none">Export</button></div>';
    floatLayer.innerHTML = formHtml;
    window.document.body.append(floatLayer);
    //document.write('<div style="z-order:100; position: -webkit-sticky; position: sticky; left:10px; top:10px; background-color:white">Button</div>');

    // 隐藏原来的内容
    window.document.getElementById('g_new_navbar').style.display = 'none';
    window.document.getElementById('micloud_main_frame').style.display = 'none';

    window.userId = '';
    window.dump_sms_data = [];

    function getCookie(cname) {
      var name = cname + "=";
      var ca = document.cookie.split(';');
      for(var i=0; i<ca.length; i++) {
            var c = ca.trim();
            if (c.indexOf(name)==0) return c.substring(name.length,c.length);
      }
      return "";
    }

    function downFile() {
      // 创建a标签
      var elementA = document.createElement('a');

      //文件的名称为时间戳加文件名后缀
      elementA.download = 'sms_' + Date.now() + ".json";
      elementA.style.display = 'none';

      //生成一个blob二进制数据,内容为json数据
      var blob = new Blob();

      //生成一个指向blob的URL地址,并赋值给a标签的href属性
      elementA.href = URL.createObjectURL(blob);
      document.body.appendChild(elementA);
      elementA.click();
      document.body.removeChild(elementA);
    }

    function loadThread(syncTag, syncThreadTag) {
      var request_data = {
            syncTag: syncTag,
            limit: 20,
            _dc: Date.now(),
            readMode: 'older',
            withPhoneCall: 'true',
            uuid: window.userId
      };
      if(syncTag !== 0) {
            request_data.syncThreadTag = syncThreadTag;
      }

      console.log("GET tag=" + syncTag + ", thread_tag=" + syncThreadTag);
      $.get('/sms/' + window.userId + '/full/thread', request_data, function(data, status, xhr) {
            if(status === 'success' && data.result === 'ok') {
                if(data.data.entries.length !== 0) {
                  $.each(data.data.entries, function(index, entry) {
                        entry.detail = [];
                        window.dump_sms_data.push(entry);
                  });
                  $('#threads').val(window.dump_sms_data.length);
                  let nextTag = data.data.watermark.syncTag;
                  let nextThreadTag = data.data.watermark.syncThreadTag;
                  window.setTimeout(function() {loadThread(nextTag, nextThreadTag)}, 200);
                } else {
                  $('#status_line').text('概要列表加载完成,开始读取短信详情...');
                  window.setTimeout(function() {loadDetails(0, '')}, 5000);
                }
            } else {
                $('#status_line').text('请求出错,检查控制台获取错误信息!');
            }
      });
    }

    function loadDetails(n, syncTag) {
      if(window.dump_sms_data.length <= n) {
            $('#status_line').text('全部加载完成,点击 Export 下载.');
            //$('#export_button').removeAttr('disabled');
            downFile();
            return;
      }

      let threadId = window.dump_sms_data.entry.threadId;
      var request_data = {
            readMode: 'older',
            _dc: Date.now(),
            threadId: threadId,
            cnt: 50,
            uuid: window.userId
      };
      if(syncTag === '') {
            request_data.limit = 50;
            $('#threads').val((n + 1) + ' / ' + window.dump_sms_data.length);
            $('#current_title').val(window.dump_sms_data.entry.snippet);
      } else {
            request_data.syncTag = syncTag;
      }

      let pagination_url = '/sms/' + window.userId + '/full/thread/' + threadId + '/pagination';
      $.get(pagination_url, request_data, function(data, status, xhr) {
            if(status === 'success' && data.result === 'ok') {
                if(data.data.entries.length !== 0) {
                  $.each(data.data.entries, function(index, entry) {
                        window.dump_sms_data.detail.push(entry);
                  });

                  let total = window.dump_sms_data.entry.total;
                  $('#current_step').val(window.dump_sms_data.detail.length + " / " + total);

                  let nextTag = data.data.syncTag;
                  window.setTimeout(function() {loadDetails(n, nextTag)}, 200);
                } else {
                  $('#status_line').text('详情刷新完成,继续读取下一组短信...');
                  window.setTimeout(function() {loadDetails(n + 1, '')}, 200);
                }
            } else {
                $('#status_line').text('请求出错,检查控制台获取错误信息!');
            }
      });
    }

    $("#start_button").click(function() {
      window.userId = getCookie('userId');
      $('#status_line').text("开始读取短信,请勿刷新页面...");
      $('#user_id').val(window.userId);

      loadThread(0, '');
    });

    $('#export_button').click(function() {
      downFile();
    });
})();


然后去小米云平台 i.mi.com 用个人账户登录,登录以后点击“短信”图标,就可以看到下面这个界面:


有时候可能不会出现,确保新的脚本已启用,多刷新几次就好。
直接点击 “Start”开始读取短信,读取完成后,自动会下载一个 Json 文件,文件名像是 "sms_1599410995324.json" 这样。
读取过程中界面显示如下:


然后去要导入短信的手机,用 “SMS Backup & Restore” 软件备份短信到本地目录,备份文件默认就在手机的 SMSBackupRestore 目录,文件名类似于 "sms-20200907111414.xml"


下载附件中的改名为 sms_merge.py,确保 python3 环境已经安装好了,然后把 前面产生的 json、xml 和 py 脚本放在一个目录里,打开 cmd 或 powershell,切换到这个目录。

假设 json 文件名为 sms_1599410995324.json,xml 文件名为 sms-20200907111414.xml,在命令行里输入如下命令,其中“C:\python37”根据实际版本调整

c:\python37\python.exe sms_merge.py sms_1599410995324.json sms-20200907111414.xml sms-from-mi.xml


若执行成功,目录里会多出来一个名为 "sms-from-mi.xml"的文件,就是导出的短信了,并且与手机中已有的短信做过比较去重处理。

将导出的短信备份文件拷贝回手机,在“SMS Backup & Restore” 软件选择恢复,等待恢复完成即可。

Hmily 发表于 2020-9-9 19:06

I D:netelfz
邮箱:topskill@gmail.com

申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。

netelfz 发表于 2020-9-10 22:10

我来报道了,感谢H大通过{:1_918:}{:1_918:}
页: [1]
查看完整版本: 申请会员ID: netelfz【申请通过】