├── README.md
├── node-im
├── README
├── TimRestApiGear.js
├── TimSendGroupMsgPressTest.js
├── config
│ ├── config.js
│ └── sig_config.js
├── express-im.js
├── image
│ ├── 01.png
│ ├── 02.png
│ ├── 03.jpg
│ ├── 04.png
│ └── 05.png
├── lib
│ ├── TimGenerateSig.js
│ ├── TimRestApi.js
│ └── path
│ │ ├── private_key
│ │ └── public_key
└── package-lock.json
└── wx-tencent-im
├── app.js
├── app.json
├── app.wxss
├── pages
├── chat
│ ├── chat.js
│ ├── chat.json
│ ├── chat.wxml
│ └── chat.wxss
└── index
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
├── project.config.json
└── utils
├── base64.js
├── encrypt.js
├── im_handler.js
├── rsa.js
├── tea.js
├── tls.js
├── util.js
└── webim_wx.js
/README.md:
--------------------------------------------------------------------------------
1 | ## 微信小程序接入腾讯云 IM 即时通讯样例代码介绍
2 |
3 | 由于项目需求需要在小程序中接入腾讯云 IM 即时通讯,主要是需要实现一对一单聊的功能,我主要做的是 Java 开发,这个样例是替我们前端去爬坑写的 demo,所以代码较为简洁,适合拷贝过去修改后直接使用,样例实现了两个页面,一个是最近会话列表展示,另一个是好友会话页面展示,具体截图如下:
4 |
5 | ### 最近会话列表
6 |
7 | 
8 |
9 | ### 好友会话页面
10 |
11 | 
12 |
13 | ---
14 |
15 | ## 如何使用
16 |
17 | 样例代码包含两个项目,`node-im` 和 `wx-tencent-im`。下面会逐一介绍。
18 |
19 | ### node-im
20 |
21 | 使用腾讯云 IM 时需要通过腾讯云通讯提供的一系列 REST API 来管理你的应用,所以后端需要实现一些接口用于测试和使用,完整接口文档参考 [服务端集成指引](https://cloud.tencent.com/document/product/269/4029)。
22 |
23 | 为了快速将样例跑起来,我直接使用了 node.js 来调用腾讯云通讯提供的 REST API,要启动 `node-im` 之前,你需要 install 两个 npm 包:
24 |
25 | ``` node
26 | cnpm install expres
27 | cnpm install tls-sig-api
28 | ```
29 |
30 | 实际对接时,后端根据自己的开发语言去进行集成,有 3 个接口是必须实现的:
31 |
32 | 1. 通过 identifier 获取 sig ([通过私钥和公钥生成,保存 180 天](https://cloud.tencent.com/document/product/269/1510)), 用于前端登录 im 鉴权。
33 | 2. 将用户导入到你的腾讯云 im 应用中([独立模式帐号导入](https://cloud.tencent.com/document/product/269/1608))。
34 | 3. 双向绑定好友关系,绑定后才能一对一通讯([添加好友](https://cloud.tencent.com/document/product/269/1643))。
35 |
36 | 我在 `node-im` 中提供了这 3 个接口,另外还实现了一个消息发送的接口,方便用于测试,提供接口的 url 都可以在 `express-im.js` 文件中看到,启动服务端之前,需要修改 `config.js` 和 `sig_config.js` 的配置信息,以及公钥和私钥的文件路径,这些配置信息和文件在你创建腾讯云通讯 app 应用在应用配置里面得到。
37 |
38 | 配置信息修改完毕后,执行以下代码即可启动服务端:
39 |
40 | ``` node
41 | node express-im.js
42 | ```
43 |
44 | ### wx-tencent-im 启动
45 |
46 | 这里放置的是小程序的源码,首先介绍如何将程序跑起来:
47 |
48 | 根据官方文档的提示,首先我们需要让用户登录腾讯云 IM 应用,在登录之前,我们需要初始化一些必要参数,这些参数都在 `app.js` 中可以看到:
49 |
50 | ```
51 | App({
52 | data: {
53 | im: {
54 | sdkAppID: 1400150342, // 用户标识接入 SDK 的应用 ID,必填
55 | accountType: 36862, // 帐号体系集成中的 accountType,必填
56 | accountMode: 0, //帐号模式,0 - 独立模式 1 - 托管模式
57 | imId: null, // 用户的 id
58 | imName: null, // 用户的 im 名称
59 | imAvatarUrl: null, // 用户的 im 头像 url
60 | userSig: null // 用户通过 imId 向后台申请的签名值 sig
61 | }
62 | }
63 | })
64 | ```
65 |
66 | 这里的 imId,imName,imAvatarUrl 可以取 userInfo 的参数。然后将 imId 发送给服务端获取 userSig 的值,为了解决小程序异步问题,我将获取参数的方法封装在了 initImParams 方法中。
67 |
68 | 修改配置信息完毕后,重新编译一下,程序应该会报 [70013错误码](https://cloud.tencent.com/document/product/269/1671),这是因为你还未将用户导入到你的腾讯云 im 应用中,可以通过 Postman 先制造假数据:
69 |
70 | 
71 |
72 | 之后重新编译即可,为了能够看到数据,你需要使用 Postman 多创建几个用户,然后与当前账号绑定好友关系,然后调用聊天接口发送消息:
73 |
74 | 
75 |
76 | 
77 |
--------------------------------------------------------------------------------
/node-im/README:
--------------------------------------------------------------------------------
1 | 工具目录结构:
2 | |-- README
3 | |-- TimRestApiGear.js
4 | |-- TimSendGroupMsgPressTest.js
5 | |-- config
6 | | |-- config.js
7 | `-- lib
8 | |-- TimGenerateSig.js
9 | |-- TimRestApi.js
10 |
11 | config.js 为该工具所需APP基础配置信息。
12 |
13 | TimRestApiGear.js 为使用接口示例工具。
14 |
15 | TimRestApi.js 为接口具体实现;
16 | TimGenerateSig.js 用来生成usersig;
17 | TimSendGroupMsgPressTest.js 为群内发送消息压力测试脚本。
18 |
19 |
20 | 1.集成说明
21 | 支持独立模式
22 | 配置config.js文件,其中:
23 | identifier 为APP管理者账户;
24 | privateKey 为本地私钥位置;
25 | expireAfter 公钥有效时常,不填默认一个月
26 | 执行node TimRestApiGear.js 可看到接口示例工具访问命令(用法)。
27 |
28 | 2.压力测试脚本使用说明
29 | 该工具主要测试在群内发送消息时,客户端接收消息能力;
30 | 执行node TimSendGroupMsgPressTest.js 可看到压测脚本工具访问命令(用法);
31 | 压测前需要确保群组存在,并且群内人数尽可能少,减少其他因素影响。
32 |
33 |
34 |
35 | that.setData({
36 | contactList: [
37 | {
38 | "To_Account": "chenchen123",
39 | "C2cNick": "chenchen123",
40 | "C2cImage": app.data.imageUrl + "/2018/10/16/c591c60f65d0b0ff!400x400_big.jpg",
41 | "MsgTimeStamp": "一小时前",
42 | "MsgShow": "在吗1",
43 | "UnreadMsgCount": 3
44 | },
45 | {
46 | "To_Account": "test03",
47 | "C2cNick": "test03",
48 | "C2cImage": app.data.imageUrl + "/2018/10/16/5bf8c76d57e24b3c!400x400_big.jpg",
49 | "MsgTimeStamp": "一小时前",
50 | "MsgShow": "在吗1",
51 | "UnreadMsgCount": 4
52 | },
53 | {
54 | "To_Account": "test05",
55 | "C2cNick": "test05",
56 | "C2cImage": app.data.imageUrl + "/2018/10/16/f4c9decfecb3453799954bdac201c809!400x400.jpeg",
57 | "MsgTimeStamp": "一小时前",
58 | "MsgShow": "在吗1",
59 | "UnreadMsgCount": 5
60 | },
61 | ]
62 | })
63 |
64 | // index.js
65 | var util = require('../../utils/util.js'); // 转换时间插件
66 | var im = require('../../utils/webim_wx.js'); // 腾讯云 im 包
67 | var imhandler = require('../../utils/im_handler.js'); // 这个是所有 im 事件的 js
68 |
69 | // 获取应用实例
70 | const app = getApp()
71 |
72 | Page({
73 | data: {
74 | isNoData: false, // isNoData 用于判断是否无数据列表,然后页面做出无数据列表的反应 TODO 效果未实现
75 | /**
76 | * 会话列表(结构定义如下):
77 | * friendId
78 | * friendName
79 | * friendAvatarUrl
80 | * msgTime
81 | * msg
82 | * unreadMsgCount
83 | */
84 | contactList: []
85 | },
86 | onShow: function () {
87 | var that = this;
88 | wx.showLoading()
89 | // 会话列表所需参数初始化 需将当前会话好友数据清空
90 | imhandler.init({
91 | accountMode: app.data.im.accountMode,
92 | accountType: app.data.im.accountType,
93 | sdkAppID: app.data.im.sdkAppID,
94 | selType: im.SESSION_TYPE.C2C,
95 | imId: app.data.im.imId,
96 | imName: app.data.im.imName,
97 | imAvatarUrl: app.data.im.imAvatarUrl,
98 | friendId: null,
99 | friendName: null,
100 | friendAvatarUrl: null,
101 | contactListThat: that,
102 | chatThat: null
103 | })
104 | app.initImParams(function cbOk() {
105 | // 检查是否登录返回 true 和 false,不登录则重新登录
106 | if (im.checkLogin()) {
107 | that.initRecentContactList();
108 | // 初始化最近会话的消息未读数(监听新消息事件)
109 | im.syncMsgs(imhandler.onMsgNotify());
110 | } else {
111 | imhandler.login(that, app, function () {
112 | that.initRecentContactList();
113 | // 初始化最近会话的消息未读数(监听新消息事件)
114 | im.syncMsgs(imhandler.onMsgNotify());
115 | });
116 | }
117 | wx.hideLoading()
118 | })
119 | },
120 | /**
121 | * 拉取最近联系人列表
122 | */
123 | initRecentContactList: function () {
124 | console.log("[index.js]:[function initRecentContactList]: 开始拉取最近联系人列表");
125 | var that = this;
126 | // 真正获取会话列表的方法 count: 最近的会话数 ,最大可设置为 100 只获取有价值数据
127 | im.getRecentContactList({ 'Count': 10 }, function (resp) {
128 | if (resp.SessionItem && resp.SessionItem.length > 0) {
129 | var contactList = resp.SessionItem.map((item, index) => {
130 | return {
131 | "friendId": item.To_Account,
132 | "friendName": item.C2cNick,
133 | "friendAvatarUrl": item.C2cImage,
134 | "msgTime": util.getDateDiff(item.MsgTimeStamp * 1000),
135 | "msg": item.MsgShow,
136 | "unreadMsgCount": item.UnreadMsgCount
137 | }
138 | })
139 | // 设置联系人列表
140 | that.setData({
141 | contactList: contactList,
142 | isNoData: true
143 | })
144 | that.updateUnread()
145 | } else {
146 | that.setData({
147 | isNoData: false,
148 | })
149 | }
150 | })
151 | console.log("[index.js]:[function initRecentContactList]: 成功拉取最近联系人列表");
152 | },
153 | /**
154 | * 更新未读消息数
155 | */
156 | updateUnread: function () {
157 | var that = this
158 | // 还需要获取未读消息数
159 | var sessionMap = im.MsgStore.sessMap();
160 | var contactList = that.data.contactList
161 | for (var i in sessionMap) {
162 | var session = sessionMap[i]
163 | if (session.unread() > 0) {
164 | contactList = contactList.map((item, index) => {
165 | if (item.friendId === session.id()) {
166 | item.unreadMsgCount = session.unread()
167 | }
168 | return item;
169 | })
170 | }
171 | }
172 | // 设置联系人列表
173 | that.setData({
174 | contactList: contactList
175 | })
176 | },
177 | /**
178 | * go chat.wxml
179 | */
180 | linkChat: function(e) {
181 | wx.navigateTo({
182 | url: '/pages/chat/chat?friendId=' + e.currentTarget.dataset.id
183 | + '&friendName=' + e.currentTarget.dataset.name
184 | + '&friendAvatarUrl=' + e.currentTarget.dataset.image,
185 | })
186 | }
187 | })
188 |
--------------------------------------------------------------------------------
/node-im/TimRestApiGear.js:
--------------------------------------------------------------------------------
1 | var config = require('./config/config.js');
2 | var TimRestAPI = require('./lib/TimRestApi.js');
3 | var send_group_msg = function(api, serviceName, commandName, dataArray) {
4 | var i = 0;
5 | var msgFrom = dataArray[i++];
6 | var groupId = dataArray[i++];
7 | var msgText = dataArray[i++];
8 | var reqBody = {
9 | GroupId: groupId,
10 | MsgBody: [{
11 | MsgType: "TIMTextElem",
12 | From_Account: msgFrom,
13 | MsgContent: {
14 | Text: msgText
15 | }
16 | }]
17 | };
18 | api.request(serviceName, commandName, reqBody,
19 | function(err, data) {
20 | if (err) {
21 | console.log(err);
22 | return;
23 | }
24 | console.log(data);
25 | });
26 | }
27 |
28 | var send_msg = function(api, serviceName, commandName, dataArray) {
29 | var i = 0;
30 | var fromId = dataArray[i++];
31 | var toId = dataArray[i++];
32 | var msgText = dataArray[i++];
33 |
34 | var reqBody = {
35 | "To_Account": toId,
36 | //消息接收者
37 | "From_Account": fromId,
38 | //选填字段
39 | "MsgRandom": 123,
40 | //消息随机数
41 | "MsgBody": [{
42 | "MsgType": "TIMTextElem",
43 | //文本消息类型
44 | "MsgContent": {
45 | "Text": msgText //具体文本消息
46 | }
47 | }]
48 | }
49 | api.request(serviceName, commandName, reqBody,
50 | function(err, data) {
51 | if (err) {
52 | console.log(err);
53 | return;
54 | }
55 | console.log(data);
56 | });
57 | }
58 |
59 | var get_group_info = function(api, serviceName, commandName, dataArray) {
60 | var groupId = dataArray[0];
61 | var reqBody = {
62 | "GroupIdList": [groupId]
63 | }
64 | api.request(serviceName, commandName, reqBody,
65 | function(err, data) {
66 | if (err) {
67 | console.log(err);
68 | return;
69 | }
70 | console.log(data);
71 | });
72 | }
73 |
74 | function begin_process() {
75 | if (process.argv.length < 4) {
76 | console.log("usage:");
77 | console.log("node " + process.argv[1] + " msg_interface.js (server_name) (command) args...eg:");
78 | console.log("node " + process.argv[1] + " msg_interface.js openim sendmsg (account_id) (receiver) (text_content) 单发消息");
79 | console.log("node " + process.argv[1] + " msg_interface.js group_open_http_svc send_group_msg (account_id) (group_id) (text_content) 群组中发送普通消息");
80 | console.log("node " + process.argv[1] + " msg_interface.js group_open_http_svc get_group_info (group_id) 获取群组信息");
81 | console.log("注:");
82 | console.log("默认从配置文件config/config.js读取配置信息,其中:");
83 | console.log("identifier 为APP管理员账户");
84 | console.log('private_pem_path 为独立模式下私钥本地路径');
85 | return - 1;
86 | }
87 | var serviceName = process.argv[2];
88 | var commandName = process.argv[3];
89 | var commandKey = serviceName + "." + commandName;
90 |
91 | var commandName;
92 | var commadKey;
93 |
94 | // command dictionary
95 | var commandArray = {
96 | "openim.sendmsg": send_msg,
97 | "group_open_http_svc.send_group_msg": send_group_msg,
98 | "group_open_http_svc.get_group_info": get_group_info
99 | };
100 |
101 | if (!commandArray.hasOwnProperty(commandKey)) {
102 | console.log(commandKey);
103 | console.log("service_name or command_name error");
104 | return;
105 | }
106 |
107 | var dataArray = new Array();
108 | for (var i = 4; i < process.argv.length; i++) {
109 | dataArray[i - 4] = process.argv[i];
110 | }
111 |
112 | var api = new TimRestAPI(config);
113 | api.init(function(err, data) {
114 | if (err) {
115 | // deal error
116 | console.log(err);
117 | return;
118 | }
119 | commandValue = commandArray[commandKey];
120 | commandValue(api, serviceName, commandName, dataArray);
121 | });
122 | }
123 |
124 | begin_process();
125 |
126 |
--------------------------------------------------------------------------------
/node-im/TimSendGroupMsgPressTest.js:
--------------------------------------------------------------------------------
1 | var config = require('./config/config.js');
2 | var TimRestAPI = require('./lib/TimRestApi.js');
3 | var util = require('util');
4 |
5 | function begin_press_test(api, groupId, fromId, sendInterval, totalCount) {
6 | var sendedCount = 0; //count of package already send
7 | var cycleNum = 1; //loop manage num
8 | var increaseSeq = 1; //increase num, use for print local seq
9 | var beginTime = (new Date).getTime();
10 | var failNum = 0; //rsp return fail num
11 | var errNum = 0; // exception error
12 | var reqBody = {
13 | "GroupIdList": [ //groupid list
14 | groupId]
15 | }
16 | api.request("group_open_http_svc", "get_group_info", reqBody,
17 | function(err, data) // get seq before send_group_msg
18 | {
19 | if (err) {
20 | console.log(err);
21 | return;
22 | }
23 | if (data["ActionStatus"] != "OK") {
24 | console.log("fail at get_group_info before group_msg request\n");
25 | var strData = JSON.stringify(data);
26 | console.log(strData);
27 | return;
28 | }
29 | var beforeSeq = data["GroupInfo"][0].NextMsgSeq;
30 | var localSeq = increaseSeq;
31 | var timmer = setInterval(doRequest);
32 | function doRequest() {
33 | if (sendedCount++>=totalCount) {
34 | clearInterval(timmer);
35 | cycleNum--;
36 | return;
37 | }
38 | cycleNum++;
39 | var reqBody = {
40 | GroupId: groupId,
41 | MsgBody: [{
42 | MsgType: "TIMTextElem",
43 | From_Account: fromId,
44 | MsgContent: {
45 | Text: util.format("hello, local seq = %d", localSeq)
46 | }
47 | }]
48 | };
49 | var startTime = (new Date).getTime();
50 | api.request("group_open_http_svc", "send_group_msg", reqBody,
51 | function(err, data) {
52 | if (err) {
53 | console.log(err);
54 | errNum++;
55 | return;
56 | }
57 |
58 | if (data["ActionStatus"] != "OK") {
59 | failNum++;
60 | }
61 | cycleNum--;
62 | localSeq = increaseSeq++;
63 | if (!err) {
64 | var strData = JSON.stringify(data);
65 | console.log('local seq = %d, timecost = %d, response body: %s', localSeq, ((new Date).getTime() - startTime), strData);
66 | }
67 | if (cycleNum == 0) {
68 | console.log('total cost time: %d ms', (new Date).getTime() - beginTime);
69 |
70 | var reqBody = {
71 | "GroupIdList": [groupId]
72 | }
73 | api.request("group_open_http_svc", "get_group_info", reqBody,
74 | function(err, data) {
75 | if (err) {
76 | console.log(err);
77 | return;
78 | }
79 | if (data["ActionStatus"] != "OK") {
80 | console.log("fail at get_group_info after group_msg request\n");
81 | var strData = JSON.stringify(data);
82 | console.log(strData);
83 | return;
84 | }
85 | endSeq = data["GroupInfo"][0].NextMsgSeq;
86 | console.log('msg seq before: %d, after: %d', beforeSeq, endSeq);
87 | console.log('successNum is %s, totalNum is %s, errNum is %s, failNum is %s', totalCount - errNum - failNum, totalCount, errNum, failNum);
88 | });
89 | };
90 | });
91 | }
92 | });
93 | }
94 |
95 | function begin_process() {
96 | if (process.argv.length < 6) {
97 | console.log("usage:");
98 | console.log("node " + process.argv[1] + " (groupid) (from_id) (speed) (totalCount)");
99 | console.log(" groupid 群组ID,脚本在该群内发消息进行压测");
100 | console.log(" from_id 消息发送者的id");
101 | console.log(" speed 在群内每秒发送消息的条数");
102 | console.log(" totalCount 脚本将在群内发消息总条数");
103 | console.log("注: ");
104 | console.log("默认从配置文件config/config.js读取配置信息,其中:");
105 | console.log("identifier 为APP管理员账户");
106 | console.log("private_pem_path 为独立模式下私钥本地路径");
107 | return - 1;
108 | }
109 | var groupId = process.argv[2];
110 | var fromId = process.argv[3];
111 | var speed = parseInt(process.argv[4]);
112 | if (speed < 0 || speed > 150) {
113 | console.error('invalid speed (0 < speed < 150)');
114 | process.exit(0);
115 | }
116 | var sendInterval = 1000 / speed;
117 |
118 | var totalCount = parseInt(process.argv[5]);
119 | var api = new TimRestAPI(config);
120 | api.init(function(err, data) {
121 | if (err) {
122 | //deal error
123 | console.log(err);
124 | return;
125 | }
126 | begin_press_test(api, groupId, fromId, sendInterval, totalCount);
127 | });
128 | }
129 |
130 | begin_process();
131 |
--------------------------------------------------------------------------------
/node-im/config/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | sdkAppid: '1400150342',
3 | version: '201512300000',
4 | identifier: 'admin',
5 | accountType: '36862',
6 | privateKey: '/path/private_key',
7 | expireAfter: 30 * 24 * 3600,
8 | };
9 |
10 |
--------------------------------------------------------------------------------
/node-im/config/sig_config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "sdk_appid": 1400150342,
3 | "expire_after": 180 * 24 * 3600,
4 | "private_key": "../../lib/path/private_key",
5 | "public_key": "../../lib/path/public_key"
6 | }
--------------------------------------------------------------------------------
/node-im/express-im.js:
--------------------------------------------------------------------------------
1 | // express 框架
2 | // 后端服务接口参考腾讯 IM REST API 接口列表:https://cloud.tencent.com/document/product/269/1520
3 | var express = require('express');
4 | var sig = require('tls-sig-api');
5 | var app = express();
6 |
7 | // body-parser 模块,用于获取 post 提交的参数(默认支持 x-www-form-urlencoded)
8 | var bodyParser = require("body-parser");
9 | app.use(bodyParser.urlencoded({ extended: false }));
10 |
11 | // tencent im rest api
12 | var TimRestAPI = require('./lib/TimRestApi.js');
13 | var config = require('./config/config.js');
14 | var api = new TimRestAPI(config);
15 |
16 | var sigConfig = require('./config/sig_config.js');
17 |
18 | /**
19 | * 通过 identifier 获取 sig (通过私钥生成), 用于登录鉴权
20 | * parmas:
21 | * identifier - 用户 id
22 | */
23 | app.post('/generatedSig', function (req, res) {
24 | var s = new sig.Sig(sigConfig);
25 | res.end(s.genSig(req.body.identifier));
26 | })
27 |
28 | /**
29 | * 用户注册(独立模式帐号导入)
30 | * parmas:
31 | * identifier - 用户 id
32 | * nick - 用户昵称
33 | * faceurl - 用户头像 rul
34 | */
35 | app.post('/importUser', function (req, res) {
36 | api.init(function(err, data) {
37 | if (err) {
38 | console.log(err);
39 | res.end(err);
40 | return;
41 | }
42 | var reqBody = {
43 | "Identifier": req.body.identifier, // 用户名,长度不超过 32 字节
44 | "Nick": req.body.nick, // 用户昵称
45 | "FaceUrl": req.body.faceurl, // 用户头像 URL
46 | "type": 0 // 导入的均为普通账号
47 | }
48 | api.request("im_open_login_svc", "account_import", reqBody, function(err, data) {
49 | if (err) {
50 | res.end(JSON.stringify(err));
51 | return;
52 | }
53 | res.end(JSON.stringify(data));
54 | })
55 | })
56 | })
57 |
58 | /**
59 | * 添加好友
60 | * parmas:
61 | * fromAccount - 需要为该 Identifier 添加好友
62 | * toAccount - 好友的 Identifier
63 | * AddSource - 好友来源(前期可填 weixin)
64 | */
65 | app.post('/addFriend', function (req, res) {
66 | api.init(function(err, data) {
67 | if (err) {
68 | console.log(err);
69 | res.end(err);
70 | return;
71 | }
72 | var reqBody = {
73 | "From_Account": req.body.fromAccount,
74 | "AddFriendItem": [
75 | {
76 | "To_Account": req.body.toAccount,
77 | "AddSource":"AddSource_Type_" + req.body.sourceType
78 | }
79 | ],
80 | "AddType":"Add_Type_Both", // 默认双向绑定关系
81 | "ForceAddFlags":1 // 强制加好友
82 | }
83 | api.request("sns", "friend_add", reqBody, function(err, data) {
84 | if(err) {
85 | res.end(JSON.stringify(err));
86 | return;
87 | }
88 | res.end(JSON.stringify(data));
89 | })
90 | })
91 | })
92 |
93 | /**
94 | * 单发单聊消息
95 | * parmas:
96 | * SyncOtherMachine - 1 消息同步至发送方 2 不将消息同步至发送方
97 | * From_Account - 好友的 Identifier
98 | * AddSource - 好友来源(前期可填 weixin)
99 | */
100 | app.post('/sendmsg', function (req, res) {
101 | api.init(function(err, data) {
102 | if (err) {
103 | console.log(err);
104 | res.end(err);
105 | return;
106 | }
107 | var reqBody = {
108 | "SyncOtherMachine": 1, //消息同步至发送方
109 | "From_Account": req.body.fromAccount,
110 | "To_Account": req.body.toAccount,
111 | "MsgRandom": 1287657,
112 | "MsgBody": [
113 | {
114 | "MsgType": req.body.msgType,
115 | "MsgContent": {
116 | "Text": req.body.msgText
117 | }
118 | }
119 | ]
120 | }
121 | api.request("openim", "sendmsg", reqBody, function(err, data) {
122 | if(err) {
123 | res.end(JSON.stringify(err));
124 | return;
125 | }
126 | res.end(JSON.stringify(data));
127 | })
128 | })
129 | })
130 |
131 |
132 |
133 | var server = app.listen(8080, function () {
134 | console.log("服务器已启动, 地址是:http://localhost:8080");
135 | })
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/node-im/image/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ch-magic-duck/redhole-tencent-im/9a623418a39cda36bed1362ffa45eb58803f52d4/node-im/image/01.png
--------------------------------------------------------------------------------
/node-im/image/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ch-magic-duck/redhole-tencent-im/9a623418a39cda36bed1362ffa45eb58803f52d4/node-im/image/02.png
--------------------------------------------------------------------------------
/node-im/image/03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ch-magic-duck/redhole-tencent-im/9a623418a39cda36bed1362ffa45eb58803f52d4/node-im/image/03.jpg
--------------------------------------------------------------------------------
/node-im/image/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ch-magic-duck/redhole-tencent-im/9a623418a39cda36bed1362ffa45eb58803f52d4/node-im/image/04.png
--------------------------------------------------------------------------------
/node-im/image/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ch-magic-duck/redhole-tencent-im/9a623418a39cda36bed1362ffa45eb58803f52d4/node-im/image/05.png
--------------------------------------------------------------------------------
/node-im/lib/TimGenerateSig.js:
--------------------------------------------------------------------------------
1 | var crypto = require('crypto');
2 | var zlib = require('zlib');
3 | var fs = require('fs');
4 | var path = require('path');
5 |
6 | var base64url = {};
7 |
8 | base64url.unescape = function(str) {
9 | return (str + Array(5 - str.length % 4)).replace(/_/g, '=').replace(/\-/g, '/').replace(/\*/g, '+');
10 | };
11 |
12 | base64url.escape = function(str) {
13 | return str.replace(/\+/g, '*').replace(/\//g, '-').replace(/=/g, '_');
14 | };
15 |
16 | base64url.encode = function(str) {
17 | return this.escape(new Buffer(str).toString('base64'));
18 | };
19 |
20 | base64url.decode = function(str) {
21 | return new Buffer(this.unescape(str), 'base64').toString();
22 | };
23 |
24 | var Sig = function(config) {
25 | this.sdkAppid = config.sdkAppid;
26 | this.accountType = config.accountType;
27 | this.identifier = config.identifier;
28 | this.appidAt3rd = config.sdkAppid;
29 | this.expireAfter = (config.expireAfter || 30 * 24 * 3600).toString();
30 | this.expireUntil = parseInt(Date.now() / 1000) + parseInt(this.expireAfter);
31 | this.privateKey = fs.readFileSync(path.join(__dirname, config.privateKey)).toString();
32 | };
33 |
34 | Sig.prototype._genSignContent = function(obj) {
35 | var ret = '';
36 | for (var i in obj) {
37 | ret += i + ':' + obj[i] + '\n';
38 | }
39 | return ret;
40 | };
41 |
42 | Sig.prototype.genSig = function(evalSig, callback) {
43 | var obj = {
44 | 'TLS.appid_at_3rd': this.appidAt3rd,
45 | 'TLS.account_type': this.accountType,
46 | 'TLS.identifier': this.identifier,
47 | 'TLS.sdk_appid': this.sdkAppid,
48 | 'TLS.time': (Math.floor(Date.now() / 1000)).toString(),
49 | 'TLS.expire_after': this.expireAfter
50 | };
51 | var content = this._genSignContent(obj);
52 | try {
53 | var signer = crypto.createSign('sha256');
54 | signer.update(content, 'utf8');
55 | var usrsig = signer.sign(this.privateKey, 'base64');
56 | } catch(err) {
57 | callback(err);
58 | return;
59 | }
60 | obj['TLS.sig'] = usrsig;
61 | var text = JSON.stringify(obj);
62 | var compressed = zlib.deflateSync(new Buffer(text)).toString('base64');
63 | evalSig(base64url.escape(compressed), this.expireUntil);
64 | if (callback) {
65 | callback();
66 | }
67 | };
68 |
69 | module.exports = Sig;
--------------------------------------------------------------------------------
/node-im/lib/TimRestApi.js:
--------------------------------------------------------------------------------
1 | var Sig = require('./TimGenerateSig.js');
2 | var https = require('https');
3 | var util = require('util');
4 | var maxSock = 10000;
5 | var keepAliveAgent = new https.Agent({
6 | keepAlive: true,
7 | maxSockets: maxSock
8 | });
9 |
10 | var TimRestAPI = function(config) {
11 | this.sdkAppid = config.sdkAppid;
12 | this.identifier = config.identifier;
13 | this.config = config;
14 | }
15 |
16 | TimRestAPI.prototype.init = function(callback) {
17 | var self = this;
18 | // get usersig
19 | var sig = new Sig(this.config);
20 | sig.genSig(function(usersig, expireUntil) {
21 | self.usersig = usersig;
22 | self.expireUntil = expireUntil;
23 | }, callback);
24 | }
25 |
26 | TimRestAPI.prototype.request = function(serviceName, cmdName, reqBody, callback) {
27 | var self = this;
28 | if (this.expireUntil < (Date.now() / 1000)) {
29 | var sig = new Sig(this.config);
30 | sig.genSig(function(usersig, expireUntil) {
31 | self.usersig = usersig;
32 | self.expireUntil = expireUntil;
33 | });
34 | }
35 | var urlPath = util.format("/v4/%s/%s?usersig=%s&identifier=%s&sdkappid=%s&contenttype=json", serviceName, cmdName, this.usersig, this.identifier, this.sdkAppid);
36 | var requestArg = {
37 | agent: keepAliveAgent,
38 | host: 'console.tim.qq.com',
39 | method: 'post',
40 | path: urlPath
41 | }
42 | var chunkList = [];
43 | var req = https.request(requestArg,
44 | function(rsp) {
45 | rsp.setEncoding('utf8');
46 | rsp.on('data',
47 | function(chunk) {
48 | chunkList.push(chunk);
49 | });
50 | rsp.on('error',
51 | function(err) {
52 | if (callback) {
53 | callback(err);
54 | }
55 | });
56 | rsp.on('end',
57 | function() {
58 | rspBody = chunkList.join('');
59 | try {
60 | var rspJsonBody = JSON.parse(rspBody);
61 | } catch(err) {
62 | if (callback) {
63 | callback(err);
64 | }
65 | }
66 | if (callback) {
67 | callback(null, rspJsonBody);
68 | }
69 | });
70 | });
71 | req.write(JSON.stringify(reqBody));
72 | req.end();
73 | }
74 |
75 | module.exports = TimRestAPI;
76 |
--------------------------------------------------------------------------------
/node-im/lib/path/private_key:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ch-magic-duck/redhole-tencent-im/9a623418a39cda36bed1362ffa45eb58803f52d4/node-im/lib/path/private_key
--------------------------------------------------------------------------------
/node-im/lib/path/public_key:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ch-magic-duck/redhole-tencent-im/9a623418a39cda36bed1362ffa45eb58803f52d4/node-im/lib/path/public_key
--------------------------------------------------------------------------------
/node-im/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "requires": true,
3 | "lockfileVersion": 1,
4 | "dependencies": {
5 | "accepts": {
6 | "version": "1.3.5",
7 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
8 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
9 | "requires": {
10 | "mime-types": "2.1.20",
11 | "negotiator": "0.6.1"
12 | }
13 | },
14 | "append-field": {
15 | "version": "1.0.0",
16 | "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
17 | "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
18 | },
19 | "array-flatten": {
20 | "version": "1.1.1",
21 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
22 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
23 | },
24 | "body-parser": {
25 | "version": "1.18.3",
26 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
27 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
28 | "requires": {
29 | "bytes": "3.0.0",
30 | "content-type": "1.0.4",
31 | "debug": "2.6.9",
32 | "depd": "1.1.2",
33 | "http-errors": "1.6.3",
34 | "iconv-lite": "0.4.23",
35 | "on-finished": "2.3.0",
36 | "qs": "6.5.2",
37 | "raw-body": "2.3.3",
38 | "type-is": "1.6.16"
39 | }
40 | },
41 | "buffer-from": {
42 | "version": "1.1.1",
43 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
44 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
45 | },
46 | "busboy": {
47 | "version": "0.2.14",
48 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
49 | "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
50 | "requires": {
51 | "dicer": "0.2.5",
52 | "readable-stream": "1.1.14"
53 | }
54 | },
55 | "bytes": {
56 | "version": "3.0.0",
57 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
58 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
59 | },
60 | "concat-stream": {
61 | "version": "1.6.2",
62 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
63 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
64 | "requires": {
65 | "buffer-from": "1.1.1",
66 | "inherits": "2.0.3",
67 | "readable-stream": "2.3.6",
68 | "typedarray": "0.0.6"
69 | },
70 | "dependencies": {
71 | "isarray": {
72 | "version": "1.0.0",
73 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
74 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
75 | },
76 | "readable-stream": {
77 | "version": "2.3.6",
78 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
79 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
80 | "requires": {
81 | "core-util-is": "1.0.2",
82 | "inherits": "2.0.3",
83 | "isarray": "1.0.0",
84 | "process-nextick-args": "2.0.0",
85 | "safe-buffer": "5.1.2",
86 | "string_decoder": "1.1.1",
87 | "util-deprecate": "1.0.2"
88 | }
89 | },
90 | "string_decoder": {
91 | "version": "1.1.1",
92 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
93 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
94 | "requires": {
95 | "safe-buffer": "5.1.2"
96 | }
97 | }
98 | }
99 | },
100 | "content-disposition": {
101 | "version": "0.5.2",
102 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
103 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
104 | },
105 | "content-type": {
106 | "version": "1.0.4",
107 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
108 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
109 | },
110 | "cookie": {
111 | "version": "0.3.1",
112 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
113 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
114 | },
115 | "cookie-parser": {
116 | "version": "1.4.3",
117 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz",
118 | "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
119 | "requires": {
120 | "cookie": "0.3.1",
121 | "cookie-signature": "1.0.6"
122 | }
123 | },
124 | "cookie-signature": {
125 | "version": "1.0.6",
126 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
127 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
128 | },
129 | "core-util-is": {
130 | "version": "1.0.2",
131 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
132 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
133 | },
134 | "debug": {
135 | "version": "2.6.9",
136 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
137 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
138 | "requires": {
139 | "ms": "2.0.0"
140 | }
141 | },
142 | "depd": {
143 | "version": "1.1.2",
144 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
145 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
146 | },
147 | "destroy": {
148 | "version": "1.0.4",
149 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
150 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
151 | },
152 | "dicer": {
153 | "version": "0.2.5",
154 | "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
155 | "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
156 | "requires": {
157 | "readable-stream": "1.1.14",
158 | "streamsearch": "0.1.2"
159 | }
160 | },
161 | "ee-first": {
162 | "version": "1.1.1",
163 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
164 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
165 | },
166 | "encodeurl": {
167 | "version": "1.0.2",
168 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
169 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
170 | },
171 | "escape-html": {
172 | "version": "1.0.3",
173 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
174 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
175 | },
176 | "etag": {
177 | "version": "1.8.1",
178 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
179 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
180 | },
181 | "express": {
182 | "version": "4.16.4",
183 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
184 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
185 | "requires": {
186 | "accepts": "1.3.5",
187 | "array-flatten": "1.1.1",
188 | "body-parser": "1.18.3",
189 | "content-disposition": "0.5.2",
190 | "content-type": "1.0.4",
191 | "cookie": "0.3.1",
192 | "cookie-signature": "1.0.6",
193 | "debug": "2.6.9",
194 | "depd": "1.1.2",
195 | "encodeurl": "1.0.2",
196 | "escape-html": "1.0.3",
197 | "etag": "1.8.1",
198 | "finalhandler": "1.1.1",
199 | "fresh": "0.5.2",
200 | "merge-descriptors": "1.0.1",
201 | "methods": "1.1.2",
202 | "on-finished": "2.3.0",
203 | "parseurl": "1.3.2",
204 | "path-to-regexp": "0.1.7",
205 | "proxy-addr": "2.0.4",
206 | "qs": "6.5.2",
207 | "range-parser": "1.2.0",
208 | "safe-buffer": "5.1.2",
209 | "send": "0.16.2",
210 | "serve-static": "1.13.2",
211 | "setprototypeof": "1.1.0",
212 | "statuses": "1.4.0",
213 | "type-is": "1.6.16",
214 | "utils-merge": "1.0.1",
215 | "vary": "1.1.2"
216 | }
217 | },
218 | "finalhandler": {
219 | "version": "1.1.1",
220 | "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
221 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
222 | "requires": {
223 | "debug": "2.6.9",
224 | "encodeurl": "1.0.2",
225 | "escape-html": "1.0.3",
226 | "on-finished": "2.3.0",
227 | "parseurl": "1.3.2",
228 | "statuses": "1.4.0",
229 | "unpipe": "1.0.0"
230 | }
231 | },
232 | "forwarded": {
233 | "version": "0.1.2",
234 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
235 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
236 | },
237 | "fresh": {
238 | "version": "0.5.2",
239 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
240 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
241 | },
242 | "http-errors": {
243 | "version": "1.6.3",
244 | "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
245 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
246 | "requires": {
247 | "depd": "1.1.2",
248 | "inherits": "2.0.3",
249 | "setprototypeof": "1.1.0",
250 | "statuses": "1.4.0"
251 | }
252 | },
253 | "iconv-lite": {
254 | "version": "0.4.23",
255 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
256 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
257 | "requires": {
258 | "safer-buffer": "2.1.2"
259 | }
260 | },
261 | "inherits": {
262 | "version": "2.0.3",
263 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
264 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
265 | },
266 | "ipaddr.js": {
267 | "version": "1.8.0",
268 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
269 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
270 | },
271 | "isarray": {
272 | "version": "0.0.1",
273 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
274 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
275 | },
276 | "media-typer": {
277 | "version": "0.3.0",
278 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
279 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
280 | },
281 | "merge-descriptors": {
282 | "version": "1.0.1",
283 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
284 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
285 | },
286 | "methods": {
287 | "version": "1.1.2",
288 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
289 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
290 | },
291 | "mime": {
292 | "version": "1.4.1",
293 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
294 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
295 | },
296 | "mime-db": {
297 | "version": "1.36.0",
298 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
299 | "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw=="
300 | },
301 | "mime-types": {
302 | "version": "2.1.20",
303 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz",
304 | "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
305 | "requires": {
306 | "mime-db": "1.36.0"
307 | }
308 | },
309 | "minimist": {
310 | "version": "0.0.8",
311 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
312 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
313 | },
314 | "mkdirp": {
315 | "version": "0.5.1",
316 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
317 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
318 | "requires": {
319 | "minimist": "0.0.8"
320 | }
321 | },
322 | "ms": {
323 | "version": "2.0.0",
324 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
325 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
326 | },
327 | "multer": {
328 | "version": "1.4.1",
329 | "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.1.tgz",
330 | "integrity": "sha512-zzOLNRxzszwd+61JFuAo0fxdQfvku12aNJgnla0AQ+hHxFmfc/B7jBVuPr5Rmvu46Jze/iJrFpSOsD7afO8SDw==",
331 | "requires": {
332 | "append-field": "1.0.0",
333 | "busboy": "0.2.14",
334 | "concat-stream": "1.6.2",
335 | "mkdirp": "0.5.1",
336 | "object-assign": "4.1.1",
337 | "on-finished": "2.3.0",
338 | "type-is": "1.6.16",
339 | "xtend": "4.0.1"
340 | }
341 | },
342 | "negotiator": {
343 | "version": "0.6.1",
344 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
345 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
346 | },
347 | "object-assign": {
348 | "version": "4.1.1",
349 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
350 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
351 | },
352 | "on-finished": {
353 | "version": "2.3.0",
354 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
355 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
356 | "requires": {
357 | "ee-first": "1.1.1"
358 | }
359 | },
360 | "parseurl": {
361 | "version": "1.3.2",
362 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
363 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
364 | },
365 | "path-to-regexp": {
366 | "version": "0.1.7",
367 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
368 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
369 | },
370 | "process-nextick-args": {
371 | "version": "2.0.0",
372 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
373 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
374 | },
375 | "proxy-addr": {
376 | "version": "2.0.4",
377 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
378 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
379 | "requires": {
380 | "forwarded": "0.1.2",
381 | "ipaddr.js": "1.8.0"
382 | }
383 | },
384 | "qs": {
385 | "version": "6.5.2",
386 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
387 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
388 | },
389 | "range-parser": {
390 | "version": "1.2.0",
391 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
392 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
393 | },
394 | "raw-body": {
395 | "version": "2.3.3",
396 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
397 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
398 | "requires": {
399 | "bytes": "3.0.0",
400 | "http-errors": "1.6.3",
401 | "iconv-lite": "0.4.23",
402 | "unpipe": "1.0.0"
403 | }
404 | },
405 | "readable-stream": {
406 | "version": "1.1.14",
407 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
408 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
409 | "requires": {
410 | "core-util-is": "1.0.2",
411 | "inherits": "2.0.3",
412 | "isarray": "0.0.1",
413 | "string_decoder": "0.10.31"
414 | }
415 | },
416 | "safe-buffer": {
417 | "version": "5.1.2",
418 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
419 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
420 | },
421 | "safer-buffer": {
422 | "version": "2.1.2",
423 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
424 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
425 | },
426 | "send": {
427 | "version": "0.16.2",
428 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
429 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
430 | "requires": {
431 | "debug": "2.6.9",
432 | "depd": "1.1.2",
433 | "destroy": "1.0.4",
434 | "encodeurl": "1.0.2",
435 | "escape-html": "1.0.3",
436 | "etag": "1.8.1",
437 | "fresh": "0.5.2",
438 | "http-errors": "1.6.3",
439 | "mime": "1.4.1",
440 | "ms": "2.0.0",
441 | "on-finished": "2.3.0",
442 | "range-parser": "1.2.0",
443 | "statuses": "1.4.0"
444 | }
445 | },
446 | "serve-static": {
447 | "version": "1.13.2",
448 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
449 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
450 | "requires": {
451 | "encodeurl": "1.0.2",
452 | "escape-html": "1.0.3",
453 | "parseurl": "1.3.2",
454 | "send": "0.16.2"
455 | }
456 | },
457 | "setprototypeof": {
458 | "version": "1.1.0",
459 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
460 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
461 | },
462 | "statuses": {
463 | "version": "1.4.0",
464 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
465 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
466 | },
467 | "streamsearch": {
468 | "version": "0.1.2",
469 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
470 | "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
471 | },
472 | "string_decoder": {
473 | "version": "0.10.31",
474 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
475 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
476 | },
477 | "type-is": {
478 | "version": "1.6.16",
479 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
480 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
481 | "requires": {
482 | "media-typer": "0.3.0",
483 | "mime-types": "2.1.20"
484 | }
485 | },
486 | "typedarray": {
487 | "version": "0.0.6",
488 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
489 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
490 | },
491 | "unpipe": {
492 | "version": "1.0.0",
493 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
494 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
495 | },
496 | "util-deprecate": {
497 | "version": "1.0.2",
498 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
499 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
500 | },
501 | "utils-merge": {
502 | "version": "1.0.1",
503 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
504 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
505 | },
506 | "vary": {
507 | "version": "1.1.2",
508 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
509 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
510 | },
511 | "xtend": {
512 | "version": "4.0.1",
513 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
514 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
515 | }
516 | }
517 | }
518 |
--------------------------------------------------------------------------------
/wx-tencent-im/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({
3 | data: {
4 | im: {
5 | sdkAppID: 1400150342, // 用户标识接入 SDK 的应用 ID,必填
6 | accountType: 36862, // 帐号体系集成中的 accountType,必填
7 | accountMode: 0, //帐号模式,0 - 独立模式 1 - 托管模式
8 | imId: null, // 用户的 id
9 | imName: null, // 用户的 im 名称
10 | imAvatarUrl: null, // 用户的 im 头像 url
11 | userSig: null // 用户通过 imId 向后台申请的签名值 sig
12 | }
13 | },
14 | onLaunch: function () {
15 | var that = this
16 | // 获取用户信息
17 | wx.getSetting({
18 | success: res => {
19 | if (res.authSetting['scope.userInfo']) {
20 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
21 | wx.getUserInfo({
22 | success: res => {
23 | // 可以将 res 发送给后台解码出 unionId
24 | this.globalData.userInfo = res.userInfo
25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
26 | // 所以此处加入 callback 以防止这种情况
27 | if (this.userInfoReadyCallback) {
28 | this.userInfoReadyCallback(res)
29 | }
30 | }
31 | })
32 | }
33 | }
34 | })
35 | },
36 | globalData: {
37 | userInfo: null
38 | },
39 | /**
40 | * 初始化 im 参数,返回成功回调
41 | */
42 | initImParams: function (cbOk){
43 | var that = this
44 | // 登录 初始化 im 参数
45 | // 注意:如果首次使用,后台需要创建【腾讯 im】账号
46 | wx.login({
47 | success: res => {
48 | var appid = 'wx17bd33e979aa3dba'
49 | var secret = 'd37ab36b1e592674213d8a1afd2195df'
50 | var uri = '?appid=' + appid + '&secret=' + secret + '&js_code=' + res.code + '&grant_type=authorization_code'
51 | var url = 'https://api.weixin.qq.com/sns/jscode2session' + uri
52 | wx.request({
53 | url: url, method: 'GET', success: res => {
54 | // 通过 openid 获取【腾讯 im】签名值
55 | var generatedSigUrl = 'http://localhost:8080/generatedSig'
56 | var header = { "Content-Type": "application/x-www-form-urlencoded" };
57 | var data = { "identifier": res.data.openid}
58 | that.data.im.imId = res.data.openid // ocGnM4nO9kZf6WANSo7H5GGBZVk4
59 | wx.request({
60 | url: generatedSigUrl, header: header, method: "POST", data: data, success: res => {
61 | // 初始化 im 数据 初始化完毕再返回回调
62 | that.data.im.userSig = res.data
63 | // 初始化 im 数据
64 | that.data.im.imName = '鸭鸭'
65 | that.data.im.imAvatarUrl = 'https://wx.qlogo.cn/mmopen/vi_32/vcFFe9Kg2Q0YfwQuaib7sHlSI65nKraL7ibuQvq1icbkrumuWDlbSM51PShPVoialzlpkiaKudLtLia0JLeWUmFprMjg/132'
66 |
67 | cbOk()
68 | }
69 | })
70 | }
71 | })
72 | }
73 | })
74 |
75 | }
76 | })
--------------------------------------------------------------------------------
/wx-tencent-im/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages":[
3 | "pages/index/index"
4 | ],
5 | "window":{
6 | "backgroundTextStyle":"light",
7 | "navigationBarBackgroundColor": "#fff",
8 | "navigationBarTitleText": "WeChat",
9 | "navigationBarTextStyle":"black",
10 | "enablePullDownRefresh": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/wx-tencent-im/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 |
--------------------------------------------------------------------------------
/wx-tencent-im/pages/chat/chat.js:
--------------------------------------------------------------------------------
1 | var util = require('../../utils/util.js'); // 转换时间插件
2 | var im = require('../../utils/webim_wx.js'); // 腾讯云 im 包
3 | var imhandler = require('../../utils/im_handler.js'); // 这个是所有 im 事件的 js
4 | const app = getApp()
5 |
6 | Page({
7 | data: {
8 | friendId: '',
9 | friendName: '',
10 | friendAvatarUrl: '',
11 | /**
12 | * 消息集合(结构如下):
13 | * msgTime 消息时间
14 | * myself 消息发送人 1 - 自己发的 0 - 好友发的
15 | * avatarUrl 头像
16 | * msgText 消息内容
17 | */
18 | messages: [],// 消息集合
19 | complete: 0, // 是否还有历史消息可以拉取,1 - 表示没有,0 - 表示有
20 | content: '', // 输入框的文本值
21 | lock: false, // 发送消息锁 true - 加锁状态 false - 解锁状态
22 | scroll_height: wx.getSystemInfoSync().windowHeight - 54,
23 | },
24 | onLoad: function (options) {
25 | var that = this
26 | if (options) { // 设置会话列表传参过来的好友id
27 | that.setData({
28 | friendId: options.friendId,
29 | friendName: options.friendName,
30 | friendAvatarUrl: options.friendAvatarUrl
31 | })
32 | wx.setNavigationBarTitle({
33 | title: options.friendName
34 | })
35 | }
36 | that.data.messages = [] // 清空历史消息
37 | },
38 | onShow: function () {
39 | var that = this
40 | // 私聊参数初始化
41 | imhandler.init({
42 | accountMode: app.data.im.accountMode,
43 | accountType: app.data.im.accountType,
44 | sdkAppID: app.data.im.sdkappid,
45 | selType: im.SESSION_TYPE.C2C, //私聊
46 | imId: app.data.im.identifier,
47 | imName: app.data.im.imName,
48 | imAvatarUrl: app.data.im.imAvatarUrl,
49 | friendId: that.data.friendId,
50 | friendName: that.data.friendName,
51 | friendAvatarUrl: that.data.friendAvatarUrl,
52 | contactListThat: null,
53 | chatThat: that
54 | })
55 | if (im.checkLogin()) {
56 | //获取聊天历史记录
57 | imhandler.getC2CHistoryMsgs(function cbOk(result) {
58 | handlerHistoryMsgs(result, that)
59 | })
60 | } else {
61 | imhandler.sdkLogin(that, app, this.data.selToID, () => {
62 | //获取聊天历史记录
63 | imhandler.getC2CHistoryMsgs(function cbOk(result) {
64 | handlerHistoryMsgs(result, that)
65 | });
66 | });
67 | }
68 | },
69 | /**
70 | * 获取文本的消息
71 | */
72 | getContent: function (e) {
73 | var that = this;
74 | that.setData({
75 | content: e.detail.value
76 | })
77 | },
78 | /**
79 | * 发送消息
80 | */
81 | sendMsg: function (e) {
82 | debugger
83 | var that = this
84 | // 消息锁 锁定中
85 | if (that.data.lock) {
86 | wx.showToast({
87 | title: '发消息太急了,慢一点'
88 | });
89 | return
90 | }
91 | // 开始加锁
92 | that.setData({ lock: true })
93 | if (that.data.content == '' || !that.data.content.replace(/^\s*|\s*$/g, '')) {
94 | wx.showToast({
95 | title: '总得填点内容吧'
96 | });
97 | this.setData({ lock: false })
98 | return;
99 | }
100 | var content = that.data.content
101 | // 调用腾讯IM发送消息
102 | imhandler.onSendMsg(content, function cbOk() {
103 | that.addMessage(content, true, that)
104 | }, function cbErr(err) {
105 | im.Log.error("消息发送失败", err)
106 | })
107 | // 解锁
108 | this.setData({ lock: false})
109 | },
110 | /**
111 | * 发送消息
112 | */
113 | addMessage: function(msg, isSend, that) {
114 | var messages = that.data.messages;
115 | var message = {
116 | 'myself': isSend ? 1 : 0,
117 | 'avatarUrl': isSend ? app.data.im.imAvatarUrl : that.data.friendAvatarUrl,
118 | 'msgText': msg,
119 | 'msgTime': util.getDateDiff(Date.parse(new Date()))
120 | }
121 | messages.push(message);
122 | that.setData({
123 | messages: messages,
124 | content: '' // 清空输入框文本
125 | })
126 | that.scrollToBottom();
127 | },
128 | scrollToBottom: function () {
129 | this.setData({
130 | toView: 'row_' + (this.data.messages.length - 1)
131 | });
132 | }
133 | })
134 | /**
135 | * 处理历史消息
136 | */
137 | function handlerHistoryMsgs(result, that) {
138 | var historyMsgs = [];
139 | for (var i = 0; i < result.MsgList.length; i++) {
140 | var msg = result.MsgList[i]
141 | var message = {
142 | 'myself': msg.isSend ? 1 : 0,
143 | 'avatarUrl': msg.isSend ? app.data.im.imAvatarUrl : that.data.friendAvatarUrl,
144 | 'msgText': msg.elems[0].content.text,
145 | 'msgTime': util.getDateDiff(msg.time * 1000)
146 | }
147 | historyMsgs.push(message)
148 | }
149 | // 拉取消息后,可以先将下一次拉取信息所需要的数据存储起来
150 | wx.setStorageSync('lastMsgTime', result.LastMsgTime);
151 | wx.setStorageSync('msgKey', result.MsgKey);
152 | that.setData({
153 | messages: historyMsgs,
154 | complete: result.Complete
155 | })
156 | }
--------------------------------------------------------------------------------
/wx-tencent-im/pages/chat/chat.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/wx-tencent-im/pages/chat/chat.wxml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | {{item.msgTime}}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{item.msgText}}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 发送
25 |
--------------------------------------------------------------------------------
/wx-tencent-im/pages/chat/chat.wxss:
--------------------------------------------------------------------------------
1 | /** 聊天窗口样式
2 | * 54px为回复框高度,js同
3 | */
4 |
5 | /*聊天记录*/
6 | .message-list {
7 | /*margin-bottom: 54px;*/
8 | background: rgb(235, 235, 235);
9 | }
10 |
11 | /*单元行*/
12 | .row {
13 | display: flex;
14 | flex-direction: column;
15 | margin: 0 30rpx;
16 | }
17 |
18 | /*日期*/
19 | .datetime {
20 | font-size: 10px;
21 | padding: 10px 0;
22 | color: #999;
23 | text-align: center;
24 | }
25 |
26 | .send {
27 | font-size: 15px;
28 | padding-right: 10px;
29 | color: #999;
30 | text-align: center;
31 | }
32 |
33 | /*主体*/
34 | .body {
35 | display: flex;
36 | flex-direction: row;
37 | align-items: flex-start;
38 | justify-content: flex-start;
39 | width: 100%;
40 | margin-top: 10px;
41 | }
42 |
43 |
44 | /*头像容器*/
45 | .body.avatar-container {
46 | width: 20%;
47 | }
48 |
49 | /*头像*/
50 | .body .avatar {
51 | width: 80rpx;
52 | height: 80rpx;
53 | border-radius: 50%;
54 | margin: 0 20rpx;
55 | }
56 |
57 | /*文本消息*/
58 | .body .content {
59 | font-size: 16px;
60 | background: #fff;
61 | border-radius: 5px;
62 | padding: 10px;
63 | line-height: 22px;
64 | margin-bottom: 10px;
65 | }
66 |
67 | /* 三角箭头 */
68 | .body .triangle {
69 | background: white;
70 | width: 20rpx;
71 | height: 20rpx;
72 | margin-top: 26rpx;
73 | transform: rotate(45deg);
74 | position: absolute;
75 | }
76 |
77 | /*图片消息*/
78 | .picture {
79 | width: 160px;
80 | }
81 |
82 | /*回复框*/
83 | .reply {
84 | display: flex;
85 | flex-direction: row;
86 | justify-content: flex-start;
87 | align-items: center;
88 | position: fixed;
89 | bottom: 0;
90 | width: 100%;
91 | height: 54px;
92 | border-top: 1px solid rgb(215, 215, 215);
93 | background: rgb(245, 245, 245);
94 | }
95 |
96 | .reply .voice-image {
97 | width: 25px;
98 | height: 25px;
99 | margin-left: 3%;
100 | }
101 |
102 | /*文本输入或语音录入*/
103 | .reply .opration-area {
104 | flex: 1;
105 | padding: 8px;
106 | }
107 |
108 | /*回复文本框*/
109 | .reply input {
110 | background: rgb(252, 252, 252);
111 | height: 36px;
112 | border: 1px solid rgb(221, 221, 221);
113 | border-radius: 6px;
114 | padding-left: 3px;
115 | }
116 |
117 | /*选取图片*/
118 | .reply .choose-image {
119 | width: 25px;
120 | height: 25px;
121 | margin-right: 3%;
122 | }
123 |
124 | /*按住说话button*/
125 | .voice-button {
126 | height: 36px;
127 | color: #818181;
128 | font-size: 14px;
129 | line-height: 36px;
130 | }
131 |
132 | /*悬浮提示框*/
133 | .hud-container {
134 | position: fixed;
135 | width: 150px;
136 | height: 150px;
137 | left: 50%;
138 | top: 50%;
139 | margin-left: -75px;
140 | margin-top: -75px;
141 | }
142 |
143 | /*背景层*/
144 | .hud-background {
145 | position: absolute;
146 | width: 100%;
147 | height: 100%;
148 | background: #999;
149 | opacity: .8;
150 | z-index: 11;
151 | border-radius: 10px;
152 | }
153 |
154 | /*悬浮框主体*/
155 | .hud-body {
156 | position: relative;
157 | width: 100%;
158 | height: 100%;
159 | z-index: 19;
160 | display: flex;
161 | flex-direction: column;
162 | justify-content: space-between;
163 | align-items: center;
164 | }
165 |
166 | /*图标*/
167 | .hud-body image {
168 | margin-top: 20px;
169 | width: 80px;
170 | height: 80px;
171 | }
172 |
173 | /*文字*/
174 | .hud-body .tip {
175 | color: #fff;
176 | text-align: center;
177 | width: 90%;
178 | line-height: 34px;
179 | margin: 0 auto;
180 | margin-bottom: 10px;
181 | width: 90%;
182 | }
183 |
184 | .hud-body .warning {
185 | background: #cc3333;
186 | border-radius: 5px;
187 | }
188 |
--------------------------------------------------------------------------------
/wx-tencent-im/pages/index/index.js:
--------------------------------------------------------------------------------
1 | // index.js
2 | var util = require('../../utils/util.js'); // 转换时间插件
3 | var im = require('../../utils/webim_wx.js'); // 腾讯云 im 包
4 | var imhandler = require('../../utils/im_handler.js'); // 这个是所有 im 事件的 js
5 |
6 | // 获取应用实例
7 | const app = getApp()
8 |
9 | Page({
10 | data: {
11 | isNoData: false, // isNoData 用于判断是否无数据列表,然后页面做出无数据列表的反应 TODO 效果未实现
12 | /**
13 | * 会话列表(结构定义如下):
14 | * friendId
15 | * friendName
16 | * friendAvatarUrl
17 | * msgTime
18 | * msg
19 | * unreadMsgCount
20 | */
21 | contactList: []
22 | },
23 | onShow: function () {
24 | var that = this;
25 | wx.showLoading()
26 | // 会话列表所需参数初始化 需将当前会话好友数据清空
27 | imhandler.init({
28 | accountMode: app.data.im.accountMode,
29 | accountType: app.data.im.accountType,
30 | sdkAppID: app.data.im.sdkAppID,
31 | selType: im.SESSION_TYPE.C2C,
32 | imId: app.data.im.imId,
33 | imName: app.data.im.imName,
34 | imAvatarUrl: app.data.im.imAvatarUrl,
35 | friendId: null,
36 | friendName: null,
37 | friendAvatarUrl: null,
38 | contactListThat: that,
39 | chatThat: null
40 | })
41 | app.initImParams(function cbOk() {
42 | // 检查是否登录返回 true 和 false,不登录则重新登录
43 | if (im.checkLogin()) {
44 | that.initRecentContactList();
45 | // 初始化最近会话的消息未读数(监听新消息事件)
46 | im.syncMsgs(imhandler.onMsgNotify());
47 | } else {
48 | imhandler.login(that, app, function () {
49 | that.initRecentContactList();
50 | // 初始化最近会话的消息未读数(监听新消息事件)
51 | im.syncMsgs(imhandler.onMsgNotify());
52 | });
53 | }
54 | wx.hideLoading()
55 | })
56 | },
57 | /**
58 | * 拉取最近联系人列表
59 | */
60 | initRecentContactList: function () {
61 | im.Log.warn("开始拉取最近联系人列表");
62 | var that = this;
63 | // 真正获取会话列表的方法 count: 最近的会话数 ,最大可设置为 100 只获取有价值数据
64 | im.getRecentContactList({ 'Count': 10 }, function (resp) {
65 | if (resp.SessionItem && resp.SessionItem.length > 0) {
66 | var contactList = resp.SessionItem.map((item, index) => {
67 | return {
68 | "friendId": item.To_Account,
69 | "friendName": item.C2cNick,
70 | "friendAvatarUrl": item.C2cImage,
71 | "msgTime": util.getDateDiff(item.MsgTimeStamp * 1000),
72 | "msg": item.MsgShow,
73 | "unreadMsgCount": item.UnreadMsgCount
74 | }
75 | })
76 | // 设置联系人列表
77 | that.setData({
78 | contactList: contactList,
79 | isNoData: true
80 | })
81 | that.updateUnread()
82 | } else {
83 | that.setData({
84 | isNoData: false,
85 | })
86 | }
87 | })
88 | im.Log.warn("成功拉取最近联系人列表");
89 | },
90 | /**
91 | * 更新未读消息数
92 | */
93 | updateUnread: function () {
94 | var that = this
95 | // 还需要获取未读消息数
96 | var sessionMap = im.MsgStore.sessMap();
97 | var contactList = that.data.contactList
98 | for (var i in sessionMap) {
99 | var session = sessionMap[i]
100 | if (session.unread() > 0) {
101 | contactList = contactList.map((item, index) => {
102 | if (item.friendId === session.id()) {
103 | item.unreadMsgCount = session.unread()
104 | }
105 | return item;
106 | })
107 | }
108 | }
109 | // 设置联系人列表
110 | that.setData({
111 | contactList: contactList
112 | })
113 | },
114 | /**
115 | * go chat.wxml
116 | */
117 | linkChat: function(e) {
118 | wx.navigateTo({
119 | url: '/pages/chat/chat?friendId=' + e.currentTarget.dataset.id
120 | + '&friendName=' + e.currentTarget.dataset.name
121 | + '&friendAvatarUrl=' + e.currentTarget.dataset.image,
122 | })
123 | }
124 | })
125 |
--------------------------------------------------------------------------------
/wx-tencent-im/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/wx-tencent-im/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | {{item.friendName}}
7 |
8 |
9 | {{item.msgTime}}
10 |
11 | {{item.msg}}
12 | [{{item.unreadMsgCount}}条] {{item.msg}}
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/wx-tencent-im/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | /**index.wxss**/
2 | .message-sec {
3 | width: 96%;
4 | height: auto;
5 | padding-left: 4%;
6 | display: block;
7 | background: white;
8 | border-bottom: 1rpx solid #ededed;
9 | }
10 | .message-sec .child {
11 | width: 100%;
12 | height: 90rpx;
13 | padding: 25rpx 0rpx;
14 | }
15 | .message-sec .child image {
16 | width: 90rpx;
17 | height: 90rpx;
18 | display: block;
19 | left: 0rpx;
20 | }
21 |
22 | .message-sec .child em {
23 | top:25rpx;
24 | left:105rpx;
25 | right: inherit;
26 | }
27 |
28 | .message-sec .child .content {
29 | height: 90rpx;
30 | margin-left: 110rpx;
31 | padding: 0rpx 4% 15rpx 0rpx;
32 | border-bottom: 1rpx solid #ededed;
33 | }
34 |
35 | .message-sec .child .content .flex {
36 | margin: 10rpx 0rpx 5rpx 0rpx;
37 | }
38 |
39 | .message-sec .child .content .flex .flex100-5 {
40 | font-size: 28rpx;
41 | color: #303030;
42 | }
43 |
44 | .message-sec .child .content .flex .tr {
45 | color: #6a6a6a;
46 | }
47 |
48 | .message-sec .child .content .text {
49 | font-size: 24rpx;
50 | color: #989898;
51 | }
52 |
53 | .flex100-5 {
54 | flex: 0 0 50%;
55 | box-sizing: border-box;
56 | }
57 |
58 | .ellipsis {
59 | overflow: hidden;
60 | text-overflow: ellipsis;
61 | white-space: nowrap;
62 | }
63 | .flex, {
64 | display: flex;
65 | align-items: center;
66 | justify-content: space-between;
67 | }
68 | .tr {
69 | text-align: right;
70 | }
71 |
72 | .fix {
73 | position: fixed;
74 | }
75 |
76 | .abs {
77 | position: absolute;
78 | }
79 |
80 | .rel {
81 | position: relative;
82 | }
83 |
84 | .br-3 {
85 | border-radius: 3rpx;
86 | }
87 |
88 | .br-5 {
89 | border-radius: 5rpx;
90 | }
91 |
92 | .br-10 {
93 | border-radius: 10rpx;
94 | }
95 |
96 | .br-13 {
97 | border-radius: 13rpx;
98 | }
99 |
100 | .br-15 {
101 | border-radius: 15rpx;
102 | }
103 |
104 | page {
105 | background: #f4f4f8;
106 | }
107 |
108 | view,button {
109 | display: block;
110 | overflow: initial;
111 | }
--------------------------------------------------------------------------------
/wx-tencent-im/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": false,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true
12 | },
13 | "compileType": "miniprogram",
14 | "libVersion": "2.3.0",
15 | "appid": "wx17bd33e979aa3dba",
16 | "projectname": "wx-tencent-im",
17 | "debugOptions": {
18 | "hidedInDevtools": []
19 | },
20 | "isGameTourist": false,
21 | "condition": {
22 | "search": {
23 | "current": -1,
24 | "list": []
25 | },
26 | "conversation": {
27 | "current": -1,
28 | "list": []
29 | },
30 | "game": {
31 | "currentL": -1,
32 | "list": []
33 | },
34 | "miniprogram": {
35 | "current": -1,
36 | "list": []
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/wx-tencent-im/utils/base64.js:
--------------------------------------------------------------------------------
1 | /*! https://mths.be/base64 v0.1.0 by @mathias | MIT license */
2 | ;(function(root) {
3 |
4 | // Detect free variables `exports`.
5 | var freeExports = typeof exports == 'object' && exports;
6 |
7 | // Detect free variable `module`.
8 | var freeModule = typeof module == 'object' && module &&
9 | module.exports == freeExports && module;
10 |
11 | // Detect free variable `global`, from Node.js or Browserified code, and use
12 | // it as `root`.
13 | var freeGlobal = typeof global == 'object' && global;
14 | if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
15 | root = freeGlobal;
16 | }
17 |
18 | /*--------------------------------------------------------------------------*/
19 |
20 | var InvalidCharacterError = function(message) {
21 | this.message = message;
22 | };
23 | InvalidCharacterError.prototype = new Error;
24 | InvalidCharacterError.prototype.name = 'InvalidCharacterError';
25 |
26 | var error = function(message) {
27 | // Note: the error messages used throughout this file match those used by
28 | // the native `atob`/`btoa` implementation in Chromium.
29 | throw new InvalidCharacterError(message);
30 | };
31 |
32 | var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
33 | // http://whatwg.org/html/common-microsyntaxes.html#space-character
34 | var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;
35 |
36 | // `decode` is designed to be fully compatible with `atob` as described in the
37 | // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
38 | // The optimized base64-decoding algorithm used is based on @atk’s excellent
39 | // implementation. https://gist.github.com/atk/1020396
40 | var decode = function(input) {
41 | input = String(input)
42 | .replace(REGEX_SPACE_CHARACTERS, '');
43 | var length = input.length;
44 | if (length % 4 == 0) {
45 | input = input.replace(/==?$/, '');
46 | length = input.length;
47 | }
48 | if (
49 | length % 4 == 1 ||
50 | // http://whatwg.org/C#alphanumeric-ascii-characters
51 | /[^+a-zA-Z0-9/]/.test(input)
52 | ) {
53 | error(
54 | 'Invalid character: the string to be decoded is not correctly encoded.'
55 | );
56 | }
57 | var bitCounter = 0;
58 | var bitStorage;
59 | var buffer;
60 | var output = '';
61 | var position = -1;
62 | while (++position < length) {
63 | buffer = TABLE.indexOf(input.charAt(position));
64 | bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
65 | // Unless this is the first of a group of 4 characters…
66 | if (bitCounter++ % 4) {
67 | // …convert the first 8 bits to a single ASCII character.
68 | output += String.fromCharCode(
69 | 0xFF & bitStorage >> (-2 * bitCounter & 6)
70 | );
71 | }
72 | }
73 | return output;
74 | };
75 |
76 | // `encode` is designed to be fully compatible with `btoa` as described in the
77 | // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
78 | var encode = function(input) {
79 | input = String(input);
80 | if (/[^\0-\xFF]/.test(input)) {
81 | // Note: no need to special-case astral symbols here, as surrogates are
82 | // matched, and the input is supposed to only contain ASCII anyway.
83 | error(
84 | 'The string to be encoded contains characters outside of the ' +
85 | 'Latin1 range.'
86 | );
87 | }
88 | var padding = input.length % 3;
89 | var output = '';
90 | var position = -1;
91 | var a;
92 | var b;
93 | var c;
94 | var d;
95 | var buffer;
96 | // Make sure any padding is handled outside of the loop.
97 | var length = input.length - padding;
98 |
99 | while (++position < length) {
100 | // Read three bytes, i.e. 24 bits.
101 | a = input.charCodeAt(position) << 16;
102 | b = input.charCodeAt(++position) << 8;
103 | c = input.charCodeAt(++position);
104 | buffer = a + b + c;
105 | // Turn the 24 bits into four chunks of 6 bits each, and append the
106 | // matching character for each of them to the output.
107 | output += (
108 | TABLE.charAt(buffer >> 18 & 0x3F) +
109 | TABLE.charAt(buffer >> 12 & 0x3F) +
110 | TABLE.charAt(buffer >> 6 & 0x3F) +
111 | TABLE.charAt(buffer & 0x3F)
112 | );
113 | }
114 |
115 | if (padding == 2) {
116 | a = input.charCodeAt(position) << 8;
117 | b = input.charCodeAt(++position);
118 | buffer = a + b;
119 | output += (
120 | TABLE.charAt(buffer >> 10) +
121 | TABLE.charAt((buffer >> 4) & 0x3F) +
122 | TABLE.charAt((buffer << 2) & 0x3F) +
123 | '='
124 | );
125 | } else if (padding == 1) {
126 | buffer = input.charCodeAt(position);
127 | output += (
128 | TABLE.charAt(buffer >> 2) +
129 | TABLE.charAt((buffer << 4) & 0x3F) +
130 | '=='
131 | );
132 | }
133 |
134 | return output;
135 | };
136 |
137 | var base64 = {
138 | 'encode': encode,
139 | 'decode': decode,
140 | 'version': '0.1.0'
141 | };
142 |
143 | // Some AMD build optimizers, like r.js, check for specific condition patterns
144 | // like the following:
145 | if (
146 | typeof define == 'function' &&
147 | typeof define.amd == 'object' &&
148 | define.amd
149 | ) {
150 | define(function() {
151 | return base64;
152 | });
153 | } else if (freeExports && !freeExports.nodeType) {
154 | if (freeModule) { // in Node.js or RingoJS v0.8.0+
155 | freeModule.exports = base64;
156 | } else { // in Narwhal or RingoJS v0.7.0-
157 | for (var key in base64) {
158 | base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);
159 | }
160 | }
161 | } else { // in Rhino or a web browser
162 | root.base64 = base64;
163 | }
164 |
165 | }(this));
166 |
--------------------------------------------------------------------------------
/wx-tencent-im/utils/encrypt.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 | /********************************************
3 | *
4 | * 加密及算法相关
5 | *
6 | *******************************************/
7 | var hexcase = 1;
8 | var b64pad = "";
9 | var chrsz = 8;
10 | var mode = 32;
11 | var TEA = require("tea.js");
12 | var RSA = require("rsa.js");
13 | var base64 = require("base64.js");
14 | function md5(s){
15 | return hex_md5(s);
16 | }
17 | function hex_md5(s){
18 | return binl2hex(core_md5(str2binl(s), s.length * chrsz));
19 | }
20 |
21 | function str_md5(s){
22 | return binl2str(core_md5(str2binl(s), s.length * chrsz));
23 | }
24 | function hex_hmac_md5(key, data){
25 | return binl2hex(core_hmac_md5(key, data));
26 | }
27 | function b64_hmac_md5(key, data){
28 | return binl2b64(core_hmac_md5(key, data));
29 | }
30 | function str_hmac_md5(key, data){
31 | return binl2str(core_hmac_md5(key, data));
32 | }
33 | function core_md5(x, len){
34 | x[len >> 5] |= 0x80 << ((len) % 32);
35 | x[(((len + 64) >>> 9) << 4) + 14] = len;
36 |
37 | var a = 1732584193;
38 | var b = - 271733879;
39 | var c = - 1732584194;
40 | var d = 271733878;
41 |
42 | for (var i = 0; i < x.length; i += 16) {
43 | var olda = a;
44 | var oldb = b;
45 | var oldc = c;
46 | var oldd = d;
47 |
48 | a = md5_ff(a, b, c, d, x[i + 0], 7, - 680876936);
49 | d = md5_ff(d, a, b, c, x[i + 1], 12, - 389564586);
50 | c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
51 | b = md5_ff(b, c, d, a, x[i + 3], 22, - 1044525330);
52 | a = md5_ff(a, b, c, d, x[i + 4], 7, - 176418897);
53 | d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
54 | c = md5_ff(c, d, a, b, x[i + 6], 17, - 1473231341);
55 | b = md5_ff(b, c, d, a, x[i + 7], 22, - 45705983);
56 | a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
57 | d = md5_ff(d, a, b, c, x[i + 9], 12, - 1958414417);
58 | c = md5_ff(c, d, a, b, x[i + 10], 17, - 42063);
59 | b = md5_ff(b, c, d, a, x[i + 11], 22, - 1990404162);
60 | a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
61 | d = md5_ff(d, a, b, c, x[i + 13], 12, - 40341101);
62 | c = md5_ff(c, d, a, b, x[i + 14], 17, - 1502002290);
63 | b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
64 |
65 | a = md5_gg(a, b, c, d, x[i + 1], 5, - 165796510);
66 | d = md5_gg(d, a, b, c, x[i + 6], 9, - 1069501632);
67 | c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
68 | b = md5_gg(b, c, d, a, x[i + 0], 20, - 373897302);
69 | a = md5_gg(a, b, c, d, x[i + 5], 5, - 701558691);
70 | d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
71 | c = md5_gg(c, d, a, b, x[i + 15], 14, - 660478335);
72 | b = md5_gg(b, c, d, a, x[i + 4], 20, - 405537848);
73 | a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
74 | d = md5_gg(d, a, b, c, x[i + 14], 9, - 1019803690);
75 | c = md5_gg(c, d, a, b, x[i + 3], 14, - 187363961);
76 | b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
77 | a = md5_gg(a, b, c, d, x[i + 13], 5, - 1444681467);
78 | d = md5_gg(d, a, b, c, x[i + 2], 9, - 51403784);
79 | c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
80 | b = md5_gg(b, c, d, a, x[i + 12], 20, - 1926607734);
81 |
82 | a = md5_hh(a, b, c, d, x[i + 5], 4, - 378558);
83 | d = md5_hh(d, a, b, c, x[i + 8], 11, - 2022574463);
84 | c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
85 | b = md5_hh(b, c, d, a, x[i + 14], 23, - 35309556);
86 | a = md5_hh(a, b, c, d, x[i + 1], 4, - 1530992060);
87 | d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
88 | c = md5_hh(c, d, a, b, x[i + 7], 16, - 155497632);
89 | b = md5_hh(b, c, d, a, x[i + 10], 23, - 1094730640);
90 | a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
91 | d = md5_hh(d, a, b, c, x[i + 0], 11, - 358537222);
92 | c = md5_hh(c, d, a, b, x[i + 3], 16, - 722521979);
93 | b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
94 | a = md5_hh(a, b, c, d, x[i + 9], 4, - 640364487);
95 | d = md5_hh(d, a, b, c, x[i + 12], 11, - 421815835);
96 | c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
97 | b = md5_hh(b, c, d, a, x[i + 2], 23, - 995338651);
98 |
99 | a = md5_ii(a, b, c, d, x[i + 0], 6, - 198630844);
100 | d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
101 | c = md5_ii(c, d, a, b, x[i + 14], 15, - 1416354905);
102 | b = md5_ii(b, c, d, a, x[i + 5], 21, - 57434055);
103 | a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
104 | d = md5_ii(d, a, b, c, x[i + 3], 10, - 1894986606);
105 | c = md5_ii(c, d, a, b, x[i + 10], 15, - 1051523);
106 | b = md5_ii(b, c, d, a, x[i + 1], 21, - 2054922799);
107 | a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
108 | d = md5_ii(d, a, b, c, x[i + 15], 10, - 30611744);
109 | c = md5_ii(c, d, a, b, x[i + 6], 15, - 1560198380);
110 | b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
111 | a = md5_ii(a, b, c, d, x[i + 4], 6, - 145523070);
112 | d = md5_ii(d, a, b, c, x[i + 11], 10, - 1120210379);
113 | c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
114 | b = md5_ii(b, c, d, a, x[i + 9], 21, - 343485551);
115 |
116 | a = safe_add(a, olda);
117 | b = safe_add(b, oldb);
118 | c = safe_add(c, oldc);
119 | d = safe_add(d, oldd);
120 | }
121 | if (mode == 16) {
122 | return Array(b, c);
123 | }else{
124 | return Array(a, b, c, d);
125 | }
126 | }
127 | function md5_cmn(q, a, b, x, s, t){
128 | return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
129 | }
130 | function md5_ff(a, b, c, d, x, s, t){
131 | return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
132 | }
133 | function md5_gg(a, b, c, d, x, s, t){
134 | return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
135 | }
136 | function md5_hh(a, b, c, d, x, s, t){
137 | return md5_cmn(b ^ c ^ d, a, b, x, s, t);
138 | }
139 | function md5_ii(a, b, c, d, x, s, t){
140 | return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
141 | }
142 | function core_hmac_md5(key, data){
143 | var bkey = str2binl(key);
144 | if (bkey.length > 16)
145 | bkey = core_md5(bkey, key.length * chrsz);
146 |
147 | var ipad = Array(16), opad = Array(16);
148 | for (var i = 0; i < 16; i++){
149 | ipad[i] = bkey[i] ^ 0x36363636;
150 | opad[i] = bkey[i] ^ 0x5C5C5C5C;
151 | }
152 |
153 | var hash = core_md5(ipad.concat(str2binl(data)), 512+data.length * chrsz);
154 | return core_md5(opad.concat(hash), 512+128);
155 | }
156 | function safe_add(x, y){
157 | var lsw = (x & 0xFFFF) + (y & 0xFFFF);
158 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
159 | return (msw << 16) | (lsw & 0xFFFF);
160 | }
161 | function bit_rol(num, cnt){
162 | return (num << cnt) | (num >>> (32-cnt));
163 | }
164 | function str2binl(str){
165 | var bin = Array();
166 | var mask = (1 << chrsz) - 1;
167 | for (var i = 0; i < str.length * chrsz; i += chrsz)
168 | bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (i % 32);
169 | return bin;
170 | }
171 | function binl2str(bin){
172 | var str = "";
173 | var mask = (1 << chrsz) - 1;
174 | for (var i = 0; i < bin.length * 32; i += chrsz)
175 | str += String.fromCharCode((bin[i >> 5] >>> (i % 32)) & mask);
176 | return str;
177 | }
178 | function binl2hex(binarray){
179 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
180 | var str = "";
181 |
182 | for (var i = 0; i < binarray.length * 4; i++){
183 | str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8+4)) & 0xF) +
184 | hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF);
185 | }
186 | return str;
187 | }
188 | function binl2b64(binarray){
189 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
190 | var str = "";
191 | for (var i = 0; i < binarray.length * 4; i += 3){
192 | var triplet = (((binarray[i >> 2] >> 8 * (i % 4)) & 0xFF) << 16) | ((
193 | (binarray[i + 1 >> 2] >> 8 * ((i + 1) % 4)) & 0xFF) << 8) | ((binarray[i
194 | + 2 >> 2] >> 8 * ((i + 2) % 4)) & 0xFF);
195 | for (var j = 0; j < 4; j++){
196 | if (i * 8+j * 6 > binarray.length * 32)
197 | str += b64pad;
198 | else
199 | str += tab.charAt((triplet >> 6 * (3-j)) & 0x3F);
200 | }
201 | }
202 | return str;
203 | }
204 |
205 | function hexchar2bin(str){
206 | var arr = [];
207 | for(var i=0;i "1234"
246 | // 输出为十六进制字符串展示的二进制内容
247 | var saltPwd = TEA.encrypt(h1 + idLen + hexId + hexAppid + hexAcctype + vcodeLen + hexVcode);
248 | TEA.initkey(""); // reset key
249 | var pubKey = "00988f6fe99e3d7c72b8b8a1cc9563e9750f5815316de064b531a0bfaa4dd5c2a5ea1f0e9b6e87bbcd19f445a13afada991a8ef60b812c628019741e4337933fb68438d93b62a538da25884627d3d46e6c62a5a41d30a7167a3a1ce5f6ecc3353db98b14a04ce2f777f335223134a900caa74fa79d9ab2c20ce19aaac9c24a82c847fa2eed0704553f75e030d93aa721186576cf5c344015ddc384b6b37add7139531af060548be8060a4bb075cc842bb190343c7f5e0e0b03fe1ca46c29b0df0bec7345888028df47f71fe44a0bd9cb8aed6282c095a75c57b6a604600886744b2965138730b27cf7d173381f0e53523aa1ced6864c09f7cc4135d45c5d4cfcbd";
250 | return urlBase64(RSA.encrypt(hexchar2bin(saltPwd), pubKey, "10001"));
251 | }
252 |
253 | function getRSAH1(pwd) {
254 | pwd = pwd || randpwd();
255 | var h1 = md5(pwd);
256 | var pubKey = "00ccaa91239f0a10fae03522fe6fdc6194007809732b07cb89e04dee9b4fdb9186787659fdf308be6efbc8aa147ffd8b5e4d61aba8a7e40e08af759751e1acc207a3988ce381cca6dfac4c75af1acda8bb3c09dce7a3d43fc23c95eecf56ca0c0c7a7eaeb019c912877757fe23ab28ac7060ee5409da3f0b5f079901475b11ac7d6c5cea1e7bd26a324674878cc31094b62eb407247f3e7f2070bb76a919883eaa114b0a40ea1341bf99dfd131d77343fd113f3a294fc0e19d9cc06989b98a0c14677e589ac41dd414283a3cf7685089d92770e7fde43c6aa443f2822c52fdbba309ea819bea8e4c2f1fac03930081ffd5189de9f025e15c4a1c466b761ba8e7f3";
257 | return urlBase64(RSA.encrypt(hexchar2bin(h1), pubKey, "10001"));
258 | }
259 |
260 | function urlBase64(src) {
261 | src = base64.encode(hexchar2bin(src));
262 | return src.replace(/[\/\+=]/g, function(a) {return {'/':'-', '+':'*', '=':'_'}[a];});
263 | }
264 |
265 | function randpwd() {
266 | var base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
267 | var pwd = "";
268 | for (var i=0; i<16; i++) {
269 | pwd += base[Math.round(Math.random() * 1000) % base.length]
270 | }
271 |
272 | return pwd;
273 | }
274 | return {
275 | getEncPwd: getEncPwd,
276 | getRSAH1: getRSAH1,
277 | md5: md5
278 | };
279 | }();
280 |
--------------------------------------------------------------------------------
/wx-tencent-im/utils/im_handler.js:
--------------------------------------------------------------------------------
1 | var util = require('util.js'); //转换时间插件
2 | var im = require('webim_wx.js');
3 |
4 | // 需要用到的参数
5 | var accountMode, // 帐号模式,0 - 独立模式 1 - 托管模式
6 | accountType, // 帐号体系集成中的 accountType,必填
7 | sdkAppID, // 用户标识接入 SDK 的应用 ID,必填
8 | selType, // 会话类型 webim.MSG_MAX_LENGTH.C2C - 私聊 webim.SESSION_TYPE.GROUP - 群聊
9 | imId, // 用户的 id
10 | imName, // 用户的 im 名称
11 | imAvatarUrl, // 用户的 im 头像 url
12 | friendId, // 好友 id
13 | friendName, // 好友昵称
14 | friendAvatarUrl, // 好友头像
15 | currentMsgsArray, // 当前消息数组 用于增删改查
16 | contactListThat, // 当前会话列表页面对象
17 | chatThat, // 当前聊天好友页面对象
18 | selSess
19 |
20 | /**
21 | * 登录 im
22 | */
23 | function login(that, app, callback) {
24 | im.Log.warn('开始登录 im')
25 | if (!callback) callback = () => {}
26 | if (!app.data.im.imId || !app.data.im.userSig) {
27 | im.Log.error("登录 im 失败[im 数据未初始化完毕]")
28 | return
29 | }
30 | // 获取当前用户身份
31 | var loginInfo = {
32 | 'sdkAppID': app.data.im.sdkAppID, // 用户标识接入 SDK 的应用 ID,必填
33 | 'appIDAt3rd': app.data.im.sdkAppID, // App 用户使用 OAuth 授权体系分配的 Appid,必填
34 | 'accountType': app.data.im.accountType, // 帐号体系集成中的 accountType,必填
35 | 'identifier': app.data.im.imId, // 当前用户帐号,必填
36 | 'identifierNick': app.data.im.imName, // 当前用户昵称,选填
37 | 'userSig': app.data.im.userSig, // 鉴权 Token,必填
38 | }
39 | // 指定监听事件
40 | var listeners = {
41 | "onConnNotify": onConnNotify, // 监听连接状态回调变化事件,必填
42 | "onMsgNotify": onMsgNotify // 监听新消息回调变化事件,必填
43 | }
44 | //其他对象,选填
45 | var options = {
46 | 'isAccessFormalEnv': true, // 是否访问正式环境,默认访问正式,选填
47 | 'isLogOn': true // 是否开启控制台打印日志,默认开启,选填
48 | }
49 | // sdk 登录(独立模式)
50 | im.login(loginInfo, listeners, options, function (resp) {
51 | im.Log.warn('登录 im 成功')
52 | callback()
53 | }, function (err) {
54 | im.Log.error("登录 im 失败", err.ErrorInfo)
55 | })
56 | }
57 |
58 | /**
59 | * 监听连接状态回调变化事件
60 | */
61 | function onConnNotify(resp) {
62 | switch (resp.ErrorCode) {
63 | case im.CONNECTION_STATUS.ON:
64 | im.Log.warn('连接状态正常...')
65 | break
66 | case im.CONNECTION_STATUS.OFF:
67 | im.Log.warn('连接已断开,无法收到新消息,请检查下你的网络是否正常')
68 | break
69 | default:
70 | im.Log.error('未知连接状态,status=' + resp.ErrorCode)
71 | break
72 | }
73 | }
74 |
75 | /**
76 | * 监听新消息(初始化时会获取所有会话数组,随后只获取新会话数组)
77 | * newMsgList - 新消息数组
78 | */
79 | function onMsgNotify(newMsgList) {
80 | var newMsg, session;
81 | // 如果有新消息,并且处在聊天界面上
82 | if(newMsgList && chatThat) {
83 | for (var j in newMsgList) {
84 | newMsg = newMsgList[j];
85 | if (chatThat && newMsg.getSession().id() == friendId) {
86 | selSess = newMsg.getSession()
87 | chatThat.addMessage(newMsg.elems[0].content.text, false, chatThat)
88 | }
89 | }
90 | }
91 | // 如果有新消息,并且处在会话列表界面上
92 | if (newMsgList && contactListThat) {
93 | contactListThat.initRecentContactList()
94 | }
95 | }
96 |
97 | /**
98 | * 获取聊天历史记录
99 | */
100 | function getC2CHistoryMsgs(cbOk) {
101 | im.Log.warn('开始获取聊天历史记录')
102 | var that = this
103 | currentMsgsArray = []
104 | var reqMsgCount = 10 // 拉取消息条数
105 | var lastMsgTime = wx.setStorageSync('lastMsgTime') || 0 // 最后一次拉取历史消息的时间
106 | var msgKey = wx.getStorageSync('msgKey') || ''
107 | var options = {
108 | 'Peer_Account': friendId, // 好友帐号
109 | 'MaxCnt': reqMsgCount, // 拉取消息条数
110 | 'LastMsgTime': lastMsgTime, // 最近的消息时间,即从这个时间点向前拉取历史消息
111 | 'MsgKey': msgKey
112 | }
113 | // 真正获取历史消息的方法 交由实际调用者处理数据
114 | im.getC2CHistoryMsgs(options, function (resp) {
115 | cbOk(resp)
116 | })
117 | //消息已读上报,以及设置会话自动已读标记
118 | var sessMap = im.MsgStore.sessMap()
119 | for(var i in sessMap) {
120 | var sess = sessMap[i];
121 | if (friendId == sess.id()) {
122 | im.setAutoRead(sess, true, true)
123 | }
124 | }
125 | im.Log.warn('获取聊天历史纪录完毕')
126 | }
127 |
128 | /**
129 | * 发送消息(普通消息)
130 | */
131 | function onSendMsg(msg, cbOk, cbErr) {
132 | //获取消息内容
133 | var msgtosend = msg;
134 | // 创建会话对象
135 | if (!selSess) {
136 | selSess = new im.Session(selType, friendId, friendName, friendAvatarUrl, Math.round(new Date().getTime() / 1000));
137 | }
138 | var isSend = true;// 是否为自己发送
139 | var seq = -1; // 消息序列,-1 表示 sdk 自动生成,用于去重
140 | var random = Math.round(Math.random() * 4294967296); // 消息随机数,用于去重
141 | var msgTime = Date.parse(new Date()) / 1000; // 消息时间戳
142 | var subType = im.C2C_MSG_SUB_TYPE.COMMON; // 消息子类型 c2c 消息时,参考 c2c 消息子类型对象:im.C2C_MSG_SUB_TYPE
143 | // loginInfo.identifier 消息发送者账号,loginInfo.identifierNick 消息发送者昵称
144 | var msg = new im.Msg(selSess, isSend, seq, random, msgTime, imId, subType, imName);
145 | var textObj = new im.Msg.Elem.Text(msgtosend);
146 | msg.addText(textObj);
147 | im.sendMsg(msg, function (resp) {
148 | cbOk()
149 | }, function (err) {
150 | cbErr(err)
151 | })
152 | }
153 |
154 | /**
155 | * 初始化 im
156 | */
157 | function init(opts) {
158 | accountMode = opts.accountMode
159 | accountType = opts.accountType
160 | sdkAppID = opts.sdkAppID
161 | selType = opts.selType
162 | imId = opts.imId
163 | imName = opts.imName
164 | imAvatarUrl = opts.imAvatarUrl
165 | friendId = opts.friendId
166 | friendName = opts.friendName
167 | friendAvatarUrl = opts.friendAvatarUrl
168 | contactListThat = opts.contactListThat
169 | chatThat = opts.chatThat
170 | }
171 |
172 | module.exports = {
173 | init: init,
174 | login: login,
175 | onMsgNotify: onMsgNotify,
176 | getC2CHistoryMsgs: getC2CHistoryMsgs,
177 | onSendMsg: onSendMsg
178 | }
--------------------------------------------------------------------------------
/wx-tencent-im/utils/rsa.js:
--------------------------------------------------------------------------------
1 | /**
2 | * [Encryption rsa算法封装]
3 | */
4 | module.exports = function() {
5 | // Depends on jsbn.js and rng.js
6 |
7 | // Version 1.1: support utf-8 encoding in pkcs1pad2
8 |
9 | // convert a (hex) string to a bignum object
10 |
11 | function parseBigInt(str, r) {
12 | return new BigInteger(str, r);
13 | }
14 |
15 | function linebrk(s, n) {
16 | var ret = "";
17 | var i = 0;
18 | while (i + n < s.length) {
19 | ret += s.substring(i, i + n) + "\n";
20 | i += n;
21 | }
22 | return ret + s.substring(i, s.length);
23 | }
24 |
25 | function byte2Hex(b) {
26 | if (b < 0x10)
27 | return "0" + b.toString(16);
28 | else
29 | return b.toString(16);
30 | }
31 |
32 | // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
33 | function pkcs1pad2(s, n) {
34 | if (n < s.length + 11) { // TODO: fix for utf-8
35 | uv_alert("Message too long for RSA");
36 | return null;
37 | }
38 | var ba = new Array();
39 | var i = s.length - 1;
40 | while (i >= 0 && n > 0) {
41 | var c = s.charCodeAt(i--);
42 | ba[--n] = c;
43 | /* if(c < 128) { // encode using utf-8
44 | ba[--n] = c;
45 | }
46 | else if((c > 127) && (c < 2048)) {
47 | ba[--n] = (c & 63) | 128;
48 | ba[--n] = (c >> 6) | 192;
49 | }
50 | else {
51 | ba[--n] = (c & 63) | 128;
52 | ba[--n] = ((c >> 6) & 63) | 128;
53 | ba[--n] = (c >> 12) | 224;
54 | }*/
55 | }
56 | ba[--n] = 0;
57 | var rng = new SecureRandom();
58 | var x = new Array();
59 | while (n > 2) { // random non-zero pad
60 | x[0] = 0;
61 | while (x[0] == 0) rng.nextBytes(x);
62 | ba[--n] = x[0];
63 | }
64 | ba[--n] = 2;
65 | ba[--n] = 0;
66 | return new BigInteger(ba);
67 | }
68 |
69 | // "empty" RSA key constructor
70 | function RSAKey() {
71 | this.n = null;
72 | this.e = 0;
73 | this.d = null;
74 | this.p = null;
75 | this.q = null;
76 | this.dmp1 = null;
77 | this.dmq1 = null;
78 | this.coeff = null;
79 | }
80 |
81 | // Set the public key fields N and e from hex strings
82 | function RSASetPublic(N, E) {
83 | if (N != null && E != null && N.length > 0 && E.length > 0) {
84 | this.n = parseBigInt(N, 16);
85 | this.e = parseInt(E, 16);
86 | } else
87 | uv_alert("Invalid RSA public key");
88 | }
89 |
90 | // Perform raw public operation on "x": return x^e (mod n)
91 | function RSADoPublic(x) {
92 | return x.modPowInt(this.e, this.n);
93 | }
94 |
95 | // Return the PKCS#1 RSA encryption of "text" as an even-length hex string
96 | function RSAEncrypt(text) {
97 | var m = pkcs1pad2(text, (this.n.bitLength() + 7) >> 3);
98 | if (m == null) return null;
99 | var c = this.doPublic(m);
100 | if (c == null) return null;
101 | var h = c.toString(16);
102 | if ((h.length & 1) == 0) return h;
103 | else return "0" + h;
104 | }
105 |
106 | // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
107 | //function RSAEncryptB64(text) {
108 | // var h = this.encrypt(text);
109 | // if(h) return hex2b64(h); else return null;
110 | //}
111 |
112 | // protected
113 | RSAKey.prototype.doPublic = RSADoPublic;
114 |
115 | // public
116 | RSAKey.prototype.setPublic = RSASetPublic;
117 | RSAKey.prototype.encrypt = RSAEncrypt;
118 | //RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
119 |
120 |
121 | //==================================================jsbn.js======================================================================//
122 |
123 | // Copyright (c) 2005 Tom Wu
124 | // All Rights Reserved.
125 | // See "LICENSE" for details.
126 |
127 | // Basic JavaScript BN library - subset useful for RSA encryption.
128 |
129 | // Bits per digit
130 | var dbits;
131 |
132 | // JavaScript engine analysis
133 | var canary = 0xdeadbeefcafe;
134 | var j_lm = ((canary & 0xffffff) == 0xefcafe);
135 |
136 | // (public) Constructor
137 | function BigInteger(a, b, c) {
138 | if (a != null)
139 | if ("number" == typeof a) this.fromNumber(a, b, c);
140 | else if (b == null && "string" != typeof a) this.fromString(a, 256);
141 | else this.fromString(a, b);
142 | }
143 |
144 | // return new, unset BigInteger
145 | function nbi() {
146 | return new BigInteger(null);
147 | }
148 |
149 | // am: Compute w_j += (x*this_i), propagate carries,
150 | // c is initial carry, returns final carry.
151 | // c < 3*dvalue, x < 2*dvalue, this_i < dvalue
152 | // We need to select the fastest one that works in this environment.
153 |
154 | // am1: use a single mult and divide to get the high bits,
155 | // max digit bits should be 26 because
156 | // max internal value = 2*dvalue^2-2*dvalue (< 2^53)
157 | function am1(i, x, w, j, c, n) {
158 | while (--n >= 0) {
159 | var v = x * this[i++] + w[j] + c;
160 | c = Math.floor(v / 0x4000000);
161 | w[j++] = v & 0x3ffffff;
162 | }
163 | return c;
164 | }
165 | // am2 avoids a big mult-and-extract completely.
166 | // Max digit bits should be <= 30 because we do bitwise ops
167 | // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
168 | function am2(i, x, w, j, c, n) {
169 | var xl = x & 0x7fff,
170 | xh = x >> 15;
171 | while (--n >= 0) {
172 | var l = this[i] & 0x7fff;
173 | var h = this[i++] >> 15;
174 | var m = xh * l + h * xl;
175 | l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff);
176 | c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30);
177 | w[j++] = l & 0x3fffffff;
178 | }
179 | return c;
180 | }
181 | // Alternately, set max digit bits to 28 since some
182 | // browsers slow down when dealing with 32-bit numbers.
183 | function am3(i, x, w, j, c, n) {
184 | var xl = x & 0x3fff,
185 | xh = x >> 14;
186 | while (--n >= 0) {
187 | var l = this[i] & 0x3fff;
188 | var h = this[i++] >> 14;
189 | var m = xh * l + h * xl;
190 | l = xl * l + ((m & 0x3fff) << 14) + w[j] + c;
191 | c = (l >> 28) + (m >> 14) + xh * h;
192 | w[j++] = l & 0xfffffff;
193 | }
194 | return c;
195 | }
196 | // if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
197 | // BigInteger.prototype.am = am2;
198 | // dbits = 30;
199 | // } else if (j_lm && (navigator.appName != "Netscape")) {
200 | // BigInteger.prototype.am = am1;
201 | // dbits = 26;
202 | // } else { // Mozilla/Netscape seems to prefer am3
203 | // BigInteger.prototype.am = am3;
204 | // dbits = 28;
205 | // }
206 | BigInteger.prototype.am = am3;
207 | dbits = 28;
208 | BigInteger.prototype.DB = dbits;
209 | BigInteger.prototype.DM = ((1 << dbits) - 1);
210 | BigInteger.prototype.DV = (1 << dbits);
211 |
212 | var BI_FP = 52;
213 | BigInteger.prototype.FV = Math.pow(2, BI_FP);
214 | BigInteger.prototype.F1 = BI_FP - dbits;
215 | BigInteger.prototype.F2 = 2 * dbits - BI_FP;
216 |
217 | // Digit conversions
218 | var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
219 | var BI_RC = new Array();
220 | var rr, vv;
221 | rr = "0".charCodeAt(0);
222 | for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
223 | rr = "a".charCodeAt(0);
224 | for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
225 | rr = "A".charCodeAt(0);
226 | for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
227 |
228 | function int2char(n) {
229 | return BI_RM.charAt(n);
230 | }
231 |
232 | function intAt(s, i) {
233 | var c = BI_RC[s.charCodeAt(i)];
234 | return (c == null) ? -1 : c;
235 | }
236 |
237 | // (protected) copy this to r
238 | function bnpCopyTo(r) {
239 | for (var i = this.t - 1; i >= 0; --i) r[i] = this[i];
240 | r.t = this.t;
241 | r.s = this.s;
242 | }
243 |
244 | // (protected) set from integer value x, -DV <= x < DV
245 | function bnpFromInt(x) {
246 | this.t = 1;
247 | this.s = (x < 0) ? -1 : 0;
248 | if (x > 0) this[0] = x;
249 | else if (x < -1) this[0] = x + DV;
250 | else this.t = 0;
251 | }
252 |
253 | // return bigint initialized to value
254 | function nbv(i) {
255 | var r = nbi();
256 | r.fromInt(i);
257 | return r;
258 | }
259 |
260 | // (protected) set from string and radix
261 | function bnpFromString(s, b) {
262 | var k;
263 | if (b == 16) k = 4;
264 | else if (b == 8) k = 3;
265 | else if (b == 256) k = 8; // byte array
266 | else if (b == 2) k = 1;
267 | else if (b == 32) k = 5;
268 | else if (b == 4) k = 2;
269 | else {
270 | this.fromRadix(s, b);
271 | return;
272 | }
273 | this.t = 0;
274 | this.s = 0;
275 | var i = s.length,
276 | mi = false,
277 | sh = 0;
278 | while (--i >= 0) {
279 | var x = (k == 8) ? s[i] & 0xff : intAt(s, i);
280 | if (x < 0) {
281 | if (s.charAt(i) == "-") mi = true;
282 | continue;
283 | }
284 | mi = false;
285 | if (sh == 0)
286 | this[this.t++] = x;
287 | else if (sh + k > this.DB) {
288 | this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh;
289 | this[this.t++] = (x >> (this.DB - sh));
290 | } else
291 | this[this.t - 1] |= x << sh;
292 | sh += k;
293 | if (sh >= this.DB) sh -= this.DB;
294 | }
295 | if (k == 8 && (s[0] & 0x80) != 0) {
296 | this.s = -1;
297 | if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh;
298 | }
299 | this.clamp();
300 | if (mi) BigInteger.ZERO.subTo(this, this);
301 | }
302 |
303 | // (protected) clamp off excess high words
304 | function bnpClamp() {
305 | var c = this.s & this.DM;
306 | while (this.t > 0 && this[this.t - 1] == c) --this.t;
307 | }
308 |
309 | // (public) return string representation in given radix
310 | function bnToString(b) {
311 | if (this.s < 0) return "-" + this.negate().toString(b);
312 | var k;
313 | if (b == 16) k = 4;
314 | else if (b == 8) k = 3;
315 | else if (b == 2) k = 1;
316 | else if (b == 32) k = 5;
317 | else if (b == 4) k = 2;
318 | else return this.toRadix(b);
319 | var km = (1 << k) - 1,
320 | d, m = false,
321 | r = "",
322 | i = this.t;
323 | var p = this.DB - (i * this.DB) % k;
324 | if (i-- > 0) {
325 | if (p < this.DB && (d = this[i] >> p) > 0) {
326 | m = true;
327 | r = int2char(d);
328 | }
329 | while (i >= 0) {
330 | if (p < k) {
331 | d = (this[i] & ((1 << p) - 1)) << (k - p);
332 | d |= this[--i] >> (p += this.DB - k);
333 | } else {
334 | d = (this[i] >> (p -= k)) & km;
335 | if (p <= 0) {
336 | p += this.DB;
337 | --i;
338 | }
339 | }
340 | if (d > 0) m = true;
341 | if (m) r += int2char(d);
342 | }
343 | }
344 | return m ? r : "0";
345 | }
346 |
347 | // (public) -this
348 | function bnNegate() {
349 | var r = nbi();
350 | BigInteger.ZERO.subTo(this, r);
351 | return r;
352 | }
353 |
354 | // (public) |this|
355 | function bnAbs() {
356 | return (this.s < 0) ? this.negate() : this;
357 | }
358 |
359 | // (public) return + if this > a, - if this < a, 0 if equal
360 | function bnCompareTo(a) {
361 | var r = this.s - a.s;
362 | if (r != 0) return r;
363 | var i = this.t;
364 | r = i - a.t;
365 | if (r != 0) return r;
366 | while (--i >= 0)
367 | if ((r = this[i] - a[i]) != 0) return r;
368 | return 0;
369 | }
370 |
371 | // returns bit length of the integer x
372 | function nbits(x) {
373 | var r = 1,
374 | t;
375 | if ((t = x >>> 16) != 0) {
376 | x = t;
377 | r += 16;
378 | }
379 | if ((t = x >> 8) != 0) {
380 | x = t;
381 | r += 8;
382 | }
383 | if ((t = x >> 4) != 0) {
384 | x = t;
385 | r += 4;
386 | }
387 | if ((t = x >> 2) != 0) {
388 | x = t;
389 | r += 2;
390 | }
391 | if ((t = x >> 1) != 0) {
392 | x = t;
393 | r += 1;
394 | }
395 | return r;
396 | }
397 |
398 | // (public) return the number of bits in "this"
399 | function bnBitLength() {
400 | if (this.t <= 0) return 0;
401 | return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM));
402 | }
403 |
404 | // (protected) r = this << n*DB
405 | function bnpDLShiftTo(n, r) {
406 | var i;
407 | for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i];
408 | for (i = n - 1; i >= 0; --i) r[i] = 0;
409 | r.t = this.t + n;
410 | r.s = this.s;
411 | }
412 |
413 | // (protected) r = this >> n*DB
414 | function bnpDRShiftTo(n, r) {
415 | for (var i = n; i < this.t; ++i) r[i - n] = this[i];
416 | r.t = Math.max(this.t - n, 0);
417 | r.s = this.s;
418 | }
419 |
420 | // (protected) r = this << n
421 | function bnpLShiftTo(n, r) {
422 | var bs = n % this.DB;
423 | var cbs = this.DB - bs;
424 | var bm = (1 << cbs) - 1;
425 | var ds = Math.floor(n / this.DB),
426 | c = (this.s << bs) & this.DM,
427 | i;
428 | for (i = this.t - 1; i >= 0; --i) {
429 | r[i + ds + 1] = (this[i] >> cbs) | c;
430 | c = (this[i] & bm) << bs;
431 | }
432 | for (i = ds - 1; i >= 0; --i) r[i] = 0;
433 | r[ds] = c;
434 | r.t = this.t + ds + 1;
435 | r.s = this.s;
436 | r.clamp();
437 | }
438 |
439 | // (protected) r = this >> n
440 | function bnpRShiftTo(n, r) {
441 | r.s = this.s;
442 | var ds = Math.floor(n / this.DB);
443 | if (ds >= this.t) {
444 | r.t = 0;
445 | return;
446 | }
447 | var bs = n % this.DB;
448 | var cbs = this.DB - bs;
449 | var bm = (1 << bs) - 1;
450 | r[0] = this[ds] >> bs;
451 | for (var i = ds + 1; i < this.t; ++i) {
452 | r[i - ds - 1] |= (this[i] & bm) << cbs;
453 | r[i - ds] = this[i] >> bs;
454 | }
455 | if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs;
456 | r.t = this.t - ds;
457 | r.clamp();
458 | }
459 |
460 | // (protected) r = this - a
461 | function bnpSubTo(a, r) {
462 | var i = 0,
463 | c = 0,
464 | m = Math.min(a.t, this.t);
465 | while (i < m) {
466 | c += this[i] - a[i];
467 | r[i++] = c & this.DM;
468 | c >>= this.DB;
469 | }
470 | if (a.t < this.t) {
471 | c -= a.s;
472 | while (i < this.t) {
473 | c += this[i];
474 | r[i++] = c & this.DM;
475 | c >>= this.DB;
476 | }
477 | c += this.s;
478 | } else {
479 | c += this.s;
480 | while (i < a.t) {
481 | c -= a[i];
482 | r[i++] = c & this.DM;
483 | c >>= this.DB;
484 | }
485 | c -= a.s;
486 | }
487 | r.s = (c < 0) ? -1 : 0;
488 | if (c < -1) r[i++] = this.DV + c;
489 | else if (c > 0) r[i++] = c;
490 | r.t = i;
491 | r.clamp();
492 | }
493 |
494 | // (protected) r = this * a, r != this,a (HAC 14.12)
495 | // "this" should be the larger one if appropriate.
496 | function bnpMultiplyTo(a, r) {
497 | var x = this.abs(),
498 | y = a.abs();
499 | var i = x.t;
500 | r.t = i + y.t;
501 | while (--i >= 0) r[i] = 0;
502 | for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t);
503 | r.s = 0;
504 | r.clamp();
505 | if (this.s != a.s) BigInteger.ZERO.subTo(r, r);
506 | }
507 |
508 | // (protected) r = this^2, r != this (HAC 14.16)
509 | function bnpSquareTo(r) {
510 | var x = this.abs();
511 | var i = r.t = 2 * x.t;
512 | while (--i >= 0) r[i] = 0;
513 | for (i = 0; i < x.t - 1; ++i) {
514 | var c = x.am(i, x[i], r, 2 * i, 0, 1);
515 | if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) {
516 | r[i + x.t] -= x.DV;
517 | r[i + x.t + 1] = 1;
518 | }
519 | }
520 | if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1);
521 | r.s = 0;
522 | r.clamp();
523 | }
524 |
525 | // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
526 | // r != q, this != m. q or r may be null.
527 | function bnpDivRemTo(m, q, r) {
528 | var pm = m.abs();
529 | if (pm.t <= 0) return;
530 | var pt = this.abs();
531 | if (pt.t < pm.t) {
532 | if (q != null) q.fromInt(0);
533 | if (r != null) this.copyTo(r);
534 | return;
535 | }
536 | if (r == null) r = nbi();
537 | var y = nbi(),
538 | ts = this.s,
539 | ms = m.s;
540 | var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus
541 | if (nsh > 0) {
542 | pm.lShiftTo(nsh, y);
543 | pt.lShiftTo(nsh, r);
544 | } else {
545 | pm.copyTo(y);
546 | pt.copyTo(r);
547 | }
548 | var ys = y.t;
549 | var y0 = y[ys - 1];
550 | if (y0 == 0) return;
551 | var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0);
552 | var d1 = this.FV / yt,
553 | d2 = (1 << this.F1) / yt,
554 | e = 1 << this.F2;
555 | var i = r.t,
556 | j = i - ys,
557 | t = (q == null) ? nbi() : q;
558 | y.dlShiftTo(j, t);
559 | if (r.compareTo(t) >= 0) {
560 | r[r.t++] = 1;
561 | r.subTo(t, r);
562 | }
563 | BigInteger.ONE.dlShiftTo(ys, t);
564 | t.subTo(y, y); // "negative" y so we can replace sub with am later
565 | while (y.t < ys) y[y.t++] = 0;
566 | while (--j >= 0) {
567 | // Estimate quotient digit
568 | var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2);
569 | if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out
570 | y.dlShiftTo(j, t);
571 | r.subTo(t, r);
572 | while (r[i] < --qd) r.subTo(t, r);
573 | }
574 | }
575 | if (q != null) {
576 | r.drShiftTo(ys, q);
577 | if (ts != ms) BigInteger.ZERO.subTo(q, q);
578 | }
579 | r.t = ys;
580 | r.clamp();
581 | if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder
582 | if (ts < 0) BigInteger.ZERO.subTo(r, r);
583 | }
584 |
585 | // (public) this mod a
586 | function bnMod(a) {
587 | var r = nbi();
588 | this.abs().divRemTo(a, null, r);
589 | if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r);
590 | return r;
591 | }
592 |
593 | // Modular reduction using "classic" algorithm
594 | function Classic(m) {
595 | this.m = m;
596 | }
597 |
598 | function cConvert(x) {
599 | if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
600 | else return x;
601 | }
602 |
603 | function cRevert(x) {
604 | return x;
605 | }
606 |
607 | function cReduce(x) {
608 | x.divRemTo(this.m, null, x);
609 | }
610 |
611 | function cMulTo(x, y, r) {
612 | x.multiplyTo(y, r);
613 | this.reduce(r);
614 | }
615 |
616 | function cSqrTo(x, r) {
617 | x.squareTo(r);
618 | this.reduce(r);
619 | }
620 |
621 | Classic.prototype.convert = cConvert;
622 | Classic.prototype.revert = cRevert;
623 | Classic.prototype.reduce = cReduce;
624 | Classic.prototype.mulTo = cMulTo;
625 | Classic.prototype.sqrTo = cSqrTo;
626 |
627 | // (protected) return "-1/this % 2^DB"; useful for Mont. reduction
628 | // justification:
629 | // xy == 1 (mod m)
630 | // xy = 1+km
631 | // xy(2-xy) = (1+km)(1-km)
632 | // x[y(2-xy)] = 1-k^2m^2
633 | // x[y(2-xy)] == 1 (mod m^2)
634 | // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
635 | // should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
636 | // JS multiply "overflows" differently from C/C++, so care is needed here.
637 | function bnpInvDigit() {
638 | if (this.t < 1) return 0;
639 | var x = this[0];
640 | if ((x & 1) == 0) return 0;
641 | var y = x & 3; // y == 1/x mod 2^2
642 | y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4
643 | y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8
644 | y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16
645 | // last step - calculate inverse mod DV directly;
646 | // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
647 | y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits
648 | // we really want the negative inverse, and -DV < y < DV
649 | return (y > 0) ? this.DV - y : -y;
650 | }
651 |
652 | // Montgomery reduction
653 | function Montgomery(m) {
654 | this.m = m;
655 | this.mp = m.invDigit();
656 | this.mpl = this.mp & 0x7fff;
657 | this.mph = this.mp >> 15;
658 | this.um = (1 << (m.DB - 15)) - 1;
659 | this.mt2 = 2 * m.t;
660 | }
661 |
662 | // xR mod m
663 | function montConvert(x) {
664 | var r = nbi();
665 | x.abs().dlShiftTo(this.m.t, r);
666 | r.divRemTo(this.m, null, r);
667 | if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r);
668 | return r;
669 | }
670 |
671 | // x/R mod m
672 | function montRevert(x) {
673 | var r = nbi();
674 | x.copyTo(r);
675 | this.reduce(r);
676 | return r;
677 | }
678 |
679 | // x = x/R mod m (HAC 14.32)
680 | function montReduce(x) {
681 | while (x.t <= this.mt2) // pad x so am has enough room later
682 | x[x.t++] = 0;
683 | for (var i = 0; i < this.m.t; ++i) {
684 | // faster way of calculating u0 = x[i]*mp mod DV
685 | var j = x[i] & 0x7fff;
686 | var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM;
687 | // use am to combine the multiply-shift-add into one call
688 | j = i + this.m.t;
689 | x[j] += this.m.am(0, u0, x, i, 0, this.m.t);
690 | // propagate carry
691 | while (x[j] >= x.DV) {
692 | x[j] -= x.DV;
693 | x[++j]++;
694 | }
695 | }
696 | x.clamp();
697 | x.drShiftTo(this.m.t, x);
698 | if (x.compareTo(this.m) >= 0) x.subTo(this.m, x);
699 | }
700 |
701 | // r = "x^2/R mod m"; x != r
702 | function montSqrTo(x, r) {
703 | x.squareTo(r);
704 | this.reduce(r);
705 | }
706 |
707 | // r = "xy/R mod m"; x,y != r
708 | function montMulTo(x, y, r) {
709 | x.multiplyTo(y, r);
710 | this.reduce(r);
711 | }
712 |
713 | Montgomery.prototype.convert = montConvert;
714 | Montgomery.prototype.revert = montRevert;
715 | Montgomery.prototype.reduce = montReduce;
716 | Montgomery.prototype.mulTo = montMulTo;
717 | Montgomery.prototype.sqrTo = montSqrTo;
718 |
719 | // (protected) true iff this is even
720 | function bnpIsEven() {
721 | return ((this.t > 0) ? (this[0] & 1) : this.s) == 0;
722 | }
723 |
724 | // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
725 | function bnpExp(e, z) {
726 | if (e > 0xffffffff || e < 1) return BigInteger.ONE;
727 | var r = nbi(),
728 | r2 = nbi(),
729 | g = z.convert(this),
730 | i = nbits(e) - 1;
731 | g.copyTo(r);
732 | while (--i >= 0) {
733 | z.sqrTo(r, r2);
734 | if ((e & (1 << i)) > 0) z.mulTo(r2, g, r);
735 | else {
736 | var t = r;
737 | r = r2;
738 | r2 = t;
739 | }
740 | }
741 | return z.revert(r);
742 | }
743 |
744 | // (public) this^e % m, 0 <= e < 2^32
745 | function bnModPowInt(e, m) {
746 | var z;
747 | if (e < 256 || m.isEven()) z = new Classic(m);
748 | else z = new Montgomery(m);
749 | return this.exp(e, z);
750 | }
751 |
752 | // protected
753 | BigInteger.prototype.copyTo = bnpCopyTo;
754 | BigInteger.prototype.fromInt = bnpFromInt;
755 | BigInteger.prototype.fromString = bnpFromString;
756 | BigInteger.prototype.clamp = bnpClamp;
757 | BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
758 | BigInteger.prototype.drShiftTo = bnpDRShiftTo;
759 | BigInteger.prototype.lShiftTo = bnpLShiftTo;
760 | BigInteger.prototype.rShiftTo = bnpRShiftTo;
761 | BigInteger.prototype.subTo = bnpSubTo;
762 | BigInteger.prototype.multiplyTo = bnpMultiplyTo;
763 | BigInteger.prototype.squareTo = bnpSquareTo;
764 | BigInteger.prototype.divRemTo = bnpDivRemTo;
765 | BigInteger.prototype.invDigit = bnpInvDigit;
766 | BigInteger.prototype.isEven = bnpIsEven;
767 | BigInteger.prototype.exp = bnpExp;
768 |
769 | // public
770 | BigInteger.prototype.toString = bnToString;
771 | BigInteger.prototype.negate = bnNegate;
772 | BigInteger.prototype.abs = bnAbs;
773 | BigInteger.prototype.compareTo = bnCompareTo;
774 | BigInteger.prototype.bitLength = bnBitLength;
775 | BigInteger.prototype.mod = bnMod;
776 | BigInteger.prototype.modPowInt = bnModPowInt;
777 |
778 | // "constants"
779 | BigInteger.ZERO = nbv(0);
780 | BigInteger.ONE = nbv(1);
781 |
782 | //====================================================rng.js===================================================================//
783 | // Random number generator - requires a PRNG backend, e.g. prng4.js
784 |
785 | // For best results, put code like
786 | //
787 | // in your main HTML document.
788 |
789 | var rng_state;
790 | var rng_pool;
791 | var rng_pptr;
792 |
793 | // Mix in a 32-bit integer into the pool
794 | function rng_seed_int(x) {
795 | rng_pool[rng_pptr++] ^= x & 255;
796 | rng_pool[rng_pptr++] ^= (x >> 8) & 255;
797 | rng_pool[rng_pptr++] ^= (x >> 16) & 255;
798 | rng_pool[rng_pptr++] ^= (x >> 24) & 255;
799 | if (rng_pptr >= rng_psize) rng_pptr -= rng_psize;
800 | }
801 |
802 | // Mix in the current time (w/milliseconds) into the pool
803 | function rng_seed_time() {
804 | rng_seed_int(new Date().getTime());
805 | }
806 |
807 | // Initialize the pool with junk if needed.
808 | if (rng_pool == null) {
809 | rng_pool = new Array();
810 | rng_pptr = 0;
811 | var t;
812 | // if (navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto && window.crypto.random) {
813 | // // Extract entropy (256 bits) from NS4 RNG if available
814 | // var z = window.crypto.random(32);
815 | // for (t = 0; t < z.length; ++t)
816 | // rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
817 | // }
818 | while (rng_pptr < rng_psize) { // extract some randomness from Math.random()
819 | t = Math.floor(65536 * Math.random());
820 | rng_pool[rng_pptr++] = t >>> 8;
821 | rng_pool[rng_pptr++] = t & 255;
822 | }
823 | rng_pptr = 0;
824 | rng_seed_time();
825 | //rng_seed_int(window.screenX);
826 | //rng_seed_int(window.screenY);
827 | }
828 |
829 | function rng_get_byte() {
830 | if (rng_state == null) {
831 | rng_seed_time();
832 | rng_state = prng_newstate();
833 | rng_state.init(rng_pool);
834 | for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
835 | rng_pool[rng_pptr] = 0;
836 | rng_pptr = 0;
837 | //rng_pool = null;
838 | }
839 | // TODO: allow reseeding after first request
840 | return rng_state.next();
841 | }
842 |
843 | function rng_get_bytes(ba) {
844 | var i;
845 | for (i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
846 | }
847 |
848 | function SecureRandom() {}
849 |
850 | SecureRandom.prototype.nextBytes = rng_get_bytes;
851 |
852 |
853 | //===============================================prng4==========================================================================//
854 | // prng4.js - uses Arcfour as a PRNG
855 |
856 | function Arcfour() {
857 | this.i = 0;
858 | this.j = 0;
859 | this.S = new Array();
860 | }
861 |
862 | // Initialize arcfour context from key, an array of ints, each from [0..255]
863 | function ARC4init(key) {
864 | var i, j, t;
865 | for (i = 0; i < 256; ++i)
866 | this.S[i] = i;
867 | j = 0;
868 | for (i = 0; i < 256; ++i) {
869 | j = (j + this.S[i] + key[i % key.length]) & 255;
870 | t = this.S[i];
871 | this.S[i] = this.S[j];
872 | this.S[j] = t;
873 | }
874 | this.i = 0;
875 | this.j = 0;
876 | }
877 |
878 | function ARC4next() {
879 | var t;
880 | this.i = (this.i + 1) & 255;
881 | this.j = (this.j + this.S[this.i]) & 255;
882 | t = this.S[this.i];
883 | this.S[this.i] = this.S[this.j];
884 | this.S[this.j] = t;
885 | return this.S[(t + this.S[this.i]) & 255];
886 | }
887 |
888 | Arcfour.prototype.init = ARC4init;
889 | Arcfour.prototype.next = ARC4next;
890 |
891 | // Plug in your RNG constructor here
892 | function prng_newstate() {
893 | return new Arcfour();
894 | }
895 |
896 | // Pool size must be a multiple of 4 and greater than 32.
897 | // An array of bytes the size of the pool will be passed to init()
898 | var rng_psize = 256;
899 |
900 | //rsa加密
901 | function rsa_encrypt(rawValue, key, mod) {
902 | //公钥
903 | key = key || "F20CE00BAE5361F8FA3AE9CEFA495362FF7DA1BA628F64A347F0A8C012BF0B254A30CD92ABFFE7A6EE0DC424CB6166F8819EFA5BCCB20EDFB4AD02E412CCF579B1CA711D55B8B0B3AEB60153D5E0693A2A86F3167D7847A0CB8B00004716A9095D9BADC977CBB804DBDCBA6029A9710869A453F27DFDDF83C016D928B3CBF4C7";
904 | mod = mod || "3";
905 | var _RSA = new RSAKey(); //生成rsa加密对象
906 | _RSA.setPublic(key, mod); //设置公钥和mod,PublicKey是1(2)中打印的hex值
907 | return _RSA.encrypt(rawValue);
908 | }
909 | return {
910 | encrypt: rsa_encrypt
911 | }
912 | }();
913 |
--------------------------------------------------------------------------------
/wx-tencent-im/utils/tea.js:
--------------------------------------------------------------------------------
1 | var base64 = require("base64.js");
2 | var __key = '',
3 | __pos = 0,
4 | __plain = [],
5 | __prePlain = [],
6 | __cryptPos = 0, // 当前密文块位置
7 | __preCryptPos = 0, // 上一个密文块位置
8 | __out = [], // 保存加密/解密的输出
9 | __cipher = [], // 输出的密文
10 | /*用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的,
11 | 但是最开始的8个字节没有反馈可用,所有需要标明这种情况*/
12 | __header = true;
13 | function __rand() {
14 | return Math.round(Math.random()*0xffffffff);
15 | }
16 | /**
17 | * 将数据转化为无符号整形
18 | */
19 | function __getUInt(data, offset, len) {
20 | if (!len || len > 4)
21 | len = 4;
22 | var ret = 0;
23 | for (var i=offset; i>> 0; // 无符号化
28 | }
29 | /**
30 | 把整形数据填充到数组里,要注意端序
31 | */
32 | function __intToBytes(data, offset, value) {
33 | data[offset+3] = (value >> 0) & 0xff;
34 | data[offset+2] = (value >> 8) & 0xff;
35 | data[offset+1] = (value >> 16) & 0xff;
36 | data[offset+0] = (value >> 24) & 0xff;
37 | }
38 | function __bytesInStr(data) {
39 | if (!data) return "";
40 | var outInHex = "";
41 | for (var i=0; i 0x0 && code <= 0x7f){
71 | //单字节
72 | //UTF-16 0000 - 007F
73 | //UTF-8 0xxxxxxx
74 | ret.push(s.charAt(i));
75 | }else if(code >= 0x80 && code <= 0x7ff){
76 | //双字节
77 | //UTF-16 0080 - 07FF
78 | //UTF-8 110xxxxx 10xxxxxx
79 | ret.push(
80 | //110xxxxx
81 | String.fromCharCode(0xc0 | ((code >> 6) & 0x1f)),
82 | //10xxxxxx
83 | String.fromCharCode(0x80 | (code & 0x3f))
84 | );
85 | }else if(code >= 0x800 && code <= 0xffff){
86 | //三字节
87 | //UTF-16 0800 - FFFF
88 | //UTF-8 1110xxxx 10xxxxxx 10xxxxxx
89 | ret.push(
90 | //1110xxxx
91 | String.fromCharCode(0xe0 | ((code >> 12) & 0xf)),
92 | //10xxxxxx
93 | String.fromCharCode(0x80 | ((code >> 6) & 0x3f)),
94 | //10xxxxxx
95 | String.fromCharCode(0x80 | (code & 0x3f))
96 | );
97 | }
98 | }
99 |
100 | return ret.join('');
101 | }
102 |
103 | function __encrypt(data) {
104 | __plain = new Array(8);
105 | __prePlain = new Array(8);
106 | __cryptPos = __preCryptPos = 0;
107 | __header = true;
108 | __pos = 0;
109 | var len = data.length;
110 | var padding = 0;
111 |
112 | __pos = (len + 0x0A) % 8;
113 | if (__pos != 0)
114 | __pos = 8 - __pos;
115 | __out = new Array(len + __pos + 10);
116 | __plain[0] = ((__rand() & 0xF8) | __pos ) & 0xFF;
117 |
118 | for (var i=1; i<=__pos; i++)
119 | __plain[i] = __rand() & 0xFF;
120 | __pos++;
121 |
122 | for (var i=0; i<8; i++)
123 | __prePlain[i] = 0;
124 |
125 | padding = 1;
126 | while (padding <= 2) {
127 | if (__pos < 8) {
128 | __plain[__pos++] = __rand() & 0xFF;
129 | padding++;
130 | }
131 | if (__pos == 8)
132 | __encrypt8bytes();
133 | }
134 |
135 | var i = 0;
136 | while (len > 0) {
137 | if (__pos < 8) {
138 | __plain[__pos++] = data[i++];
139 | len--;
140 | }
141 | if (__pos == 8)
142 | __encrypt8bytes();
143 | }
144 |
145 | padding = 1;
146 | while (padding <= 7) {
147 | if (__pos < 8) {
148 | __plain[__pos++] = 0;
149 | padding++;
150 | }
151 | if (__pos == 8)
152 | __encrypt8bytes();
153 | }
154 |
155 | return __out;
156 | }
157 | function __decrypt(data) {
158 | var count = 0;
159 | var m = new Array(8);
160 | var len = data.length;
161 | __cipher = data;
162 |
163 | if (len % 8 != 0 || len < 16)
164 | return null;
165 | /* 第一个8字节,加密的时候因为prePlain是全0,所以可以直接解密,得到消息的头部,
166 | 关键是可以得到真正明文开始的位置
167 | */
168 | __prePlain = __decipher(data);
169 | __pos = __prePlain[0] & 0x7;
170 | count = len - __pos - 10; // 真正的明文长度
171 | if (count < 0)
172 | return null;
173 |
174 | // 临时的preCrypt, 与加密时对应,全0的prePlain 对应 全0的preCrypt
175 | for (var i=0; i>> 0;
264 |
265 | while (loop-- > 0) {
266 | sum += delta;
267 | sum = (sum & 0xFFFFFFFF) >>> 0;
268 | y += ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b);
269 | y = (y & 0xFFFFFFFF) >>> 0;
270 | z += ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d);
271 | z = (z & 0xFFFFFFFF) >>> 0;
272 | }
273 | var bytes = new Array(8);
274 | __intToBytes(bytes, 0, y);
275 | __intToBytes(bytes, 4, z);
276 | return bytes;
277 | }
278 | function __decipher(data) {
279 | var loop = 16;
280 | var y = __getUInt(data, 0, 4);
281 | var z = __getUInt(data, 4, 4);
282 | var a = __getUInt(__key, 0, 4);
283 | var b = __getUInt(__key, 4, 4);
284 | var c = __getUInt(__key, 8, 4);
285 | var d = __getUInt(__key, 12, 4);
286 | var sum = 0xE3779B90 >>> 0;
287 | var delta = 0x9E3779B9 >>> 0;
288 |
289 | while (loop-- > 0) {
290 | z -= ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d);
291 | z = (z & 0xFFFFFFFF) >>> 0;
292 | y -= ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b);
293 | y = (y & 0xFFFFFFFF) >>> 0;
294 | sum -= delta;
295 | sum = (sum & 0xFFFFFFFF) >>> 0;
296 | }
297 |
298 | var bytes = new Array(8);
299 | __intToBytes(bytes, 0, y);
300 | __intToBytes(bytes, 4, z);
301 | return bytes;
302 | }
303 | function __decrypt8Bytes() {
304 | var len = __cipher.length;
305 | for (var i=0; i<8; i++) {
306 | __prePlain[i] ^= __cipher[__cryptPos + i];
307 | }
308 |
309 | __prePlain = __decipher(__prePlain);
310 |
311 | __cryptPos += 8;
312 | __pos = 0;
313 | return true;
314 | }
315 | /**
316 | * 把输入字符串转换为javascript array
317 | */
318 | function __dataFromStr(str, isASCII) {
319 | var data = [];
320 | if (isASCII) {
321 | for (var i=0; i> 16, (b10 >> 8) & 0xff, b10 & 0xff));
408 | }
409 |
410 | switch (pads) {
411 | case 1:
412 | b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6)
413 | x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
414 | break;
415 | case 2:
416 | b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
417 | x.push(String.fromCharCode(b10 >> 16));
418 | break;
419 | }
420 | return x.join('');
421 | }
422 | */
423 |
424 | base64.getbyte = function(s,i) {
425 | var x = s.charCodeAt(i);
426 | if (x > 255) {
427 | throw "INVALID_CHARACTER_ERR: DOM Exception 5";
428 | }
429 | return x;
430 | }
431 |
432 | base64.encode = function(s) {
433 | if (arguments.length != 1) {
434 | throw "SyntaxError: Not enough arguments";
435 | }
436 | var padchar = base64.PADCHAR;
437 | var alpha = base64.ALPHA;
438 | var getbyte = base64.getbyte;
439 |
440 | var i, b10;
441 | var x = [];
442 |
443 | // convert to string
444 | s = "" + s;
445 |
446 | var imax = s.length - s.length % 3;
447 |
448 | if (s.length == 0) {
449 | return s;
450 | }
451 | for (i = 0; i < imax; i += 3) {
452 | b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
453 | x.push(alpha.charAt(b10 >> 18));
454 | x.push(alpha.charAt((b10 >> 12) & 0x3F));
455 | x.push(alpha.charAt((b10 >> 6) & 0x3f));
456 | x.push(alpha.charAt(b10 & 0x3f));
457 | }
458 | switch (s.length - imax) {
459 | case 1:
460 | b10 = getbyte(s,i) << 16;
461 | x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
462 | padchar + padchar);
463 | break;
464 | case 2:
465 | b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
466 | x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
467 | alpha.charAt((b10 >> 6) & 0x3f) + padchar);
468 | break;
469 | }
470 | return x.join('');
471 | }
472 |
473 | module.exports = TEA;
474 |
--------------------------------------------------------------------------------
/wx-tencent-im/utils/tls.js:
--------------------------------------------------------------------------------
1 | var encrypt = require('encrypt.js');
2 |
3 | var sdkappid = 10001;
4 |
5 |
6 | function login(opts){
7 | var user = "user" + parseInt(Math.random(0, 1) * 1000000) ;
8 | wx.request({
9 | url: 'https://sxb.qcloud.com/sxb_dev/?svc=doll&cmd=fetchsig', //仅为示例,并非真实的接口地址
10 | data: {
11 | "id": user,
12 | "appid": sdkappid
13 | },
14 | method: 'post',
15 | header: {
16 | 'content-type': 'application/json'
17 | },
18 | success: function(res) {
19 | opts.success && opts.success({
20 | Identifier: user,
21 | UserSig: res.data.data.userSig
22 | });
23 | },
24 | fail : function(errMsg){
25 | opts.error && opts.error(errMsg);
26 | }
27 | });
28 | }
29 |
30 | module.exports = {
31 | init : function(opts){
32 | sdkappid = opts.sdkappid;
33 | },
34 | login : login
35 | };
--------------------------------------------------------------------------------
/wx-tencent-im/utils/util.js:
--------------------------------------------------------------------------------
1 | function formatTime(date) {
2 | var year = date.getFullYear()
3 | var month = date.getMonth() + 1
4 | var day = date.getDate()
5 |
6 | var hour = date.getHours()
7 | var minute = date.getMinutes()
8 | var second = date.getSeconds()
9 |
10 |
11 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
12 | }
13 |
14 | function formatNumber(n) {
15 | n = n.toString()
16 | return n[1] ? n : '0' + n
17 | }
18 |
19 | function getDateDiff(dateTimeStamp) {
20 | var result;
21 | var minute = 1000 * 60;
22 | var hour = minute * 60;
23 | var day = hour * 24;
24 | var halfamonth = day * 15;
25 | var month = day * 30;
26 | var now = new Date().getTime();
27 | var diffValue = now - dateTimeStamp;
28 | if (diffValue < 0) {
29 | return;
30 | }
31 | var monthC = diffValue / month;
32 | var weekC = diffValue / (7 * day);
33 | var dayC = diffValue / day;
34 | var hourC = diffValue / hour;
35 | var minC = diffValue / minute;
36 | if (monthC >= 1) {
37 | if (monthC <= 12)
38 | result = "" + parseInt(monthC) + "月前";
39 | else {
40 | result = "" + parseInt(monthC / 12) + "年前";
41 | }
42 | }
43 | else if (weekC >= 1) {
44 | result = "" + parseInt(weekC) + "周前";
45 | }
46 | else if (dayC >= 1) {
47 | result = "" + parseInt(dayC) + "天前";
48 | }
49 | else if (hourC >= 1) {
50 | result = "" + parseInt(hourC) + "小时前";
51 | }
52 | else if (minC >= 1) {
53 | result = "" + parseInt(minC) + "分钟前";
54 | } else {
55 | result = "刚刚";
56 | }
57 | return result;
58 | };
59 |
60 |
61 | module.exports = {
62 | formatTime: formatTime,
63 | getDateDiff: getDateDiff
64 | }
65 |
66 |
67 |
--------------------------------------------------------------------------------