yangck 6 tahun lalu
melakukan
a101719f15
24 mengubah file dengan 1669 tambahan dan 0 penghapusan
  1. 1 0
      .npmignore
  2. 22 0
      LICENSE
  3. 90 0
      README.md
  4. 2 0
      const/index.js
  5. 15 0
      const/result.js
  6. 1 0
      index.js
  7. 55 0
      lib/apis.js
  8. 76 0
      lib/chatroom.js
  9. 93 0
      lib/group.js
  10. 10 0
      lib/index.js
  11. 169 0
      lib/message.js
  12. 126 0
      lib/rongrequest.js
  13. 95 0
      lib/user.js
  14. 18 0
      lib/util.js
  15. 94 0
      package.json
  16. TEMPAT SAMPAH
      test/cn_lesson1_1.amr
  17. 21 0
      test/config.js
  18. TEMPAT SAMPAH
      test/raindrop.jpg
  19. 87 0
      test/test.chatroom.js
  20. 107 0
      test/test.group.js
  21. 410 0
      test/test.message.js
  22. 177 0
      test/test.user.js
  23. TEMPAT SAMPAH
      test/testvoice.amr
  24. TEMPAT SAMPAH
      test/testvoice.mp3

+ 1 - 0
.npmignore

@@ -0,0 +1 @@
+node_modules

+ 22 - 0
LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 融云 Rong Cloud
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

+ 90 - 0
README.md

@@ -0,0 +1,90 @@
+The RongCloud Server SDK
+------------------------
+
+[Rong Cloud](http://rongcloud.cn) is committed to providing cloud-based instant messaging services for Internet and Mobile Internet developers. It enables developers to quickly integrate instant messaging capabilities with applications without any hardware installation requirements.
+
+The server SDK is a wrapper of all the https API calls, which could be found [here](http://www.rongcloud.cn/docs/server.html)
+
+
+## Install
+
+```bash
+$ npm install rongcloud-sdk
+```
+
+## Init
+
+```js
+var rongcloudSDK = require( 'rongcloud-sdk' );
+rongcloudSDK.init( 'APP_KEY', 'APP_SECRET' );
+```
+
+## Usage
+
+
+```js
+rongcloudSDK.user.getToken( '0001', 'Lance', 'http://files.domain.com/avatar.jpg', function( err, resultText ) {
+  if( err ) {
+    // Handle the error
+  }
+  else {
+    var result = JSON.parse( resultText );
+    if( result.code === 200 ) {
+      //Handle the result.token
+    }
+  }
+} );
+
+```
+
+## Asking for a xml format response
+
+```js
+rongcloudSDK.user.getToken( '0001', 'Lance', 'http://files.domain.com/avatar.jpg', 'xml', function( err, resultText ) {
+  if( err ) {
+    // Handle the error
+  }
+  else {
+    // Handle the xml string, since Node.JS doesn't provide a native xml object, you have to handle it by yourself.
+  }
+} );
+```
+
+##  API Coverage
+The SDK covers all of the RongCloud APIs, including:
+
+[User](http://www.rongcloud.cn/docs/server.html#用户服务)
+
+[User block](http://www.rongcloud.cn/docs/server.html#用户封禁服务)
+
+[User blackList](http://www.rongcloud.cn/docs/server.html#用户黑名单服务)
+
+[Message](http://www.rongcloud.cn/docs/server.html#消息发送服务)
+
+[Messsage routing](http://www.rongcloud.cn/docs/server.html#消息路由服务)
+
+[Message history](http://www.rongcloud.cn/docs/server.html#消息历史记录服务)
+
+[Group](http://www.rongcloud.cn/docs/server.html#群组服务)
+
+[Chatroom](http://www.rongcloud.cn/docs/server.html#聊天室服务)
+
+The SDK API is designed to keep accordant with the http request path, with all the **"/"** replaced with **"."**, that is, for the https API:
+
+`https://api.cn.rong.io/user/getToken`
+
+You can call 
+
+`rongcloudSDK.user.getToken`
+
+
+## Testing
+Since most of the time the user won't be using the components for testing(they are async, underscore, mocha, should and xml2js), these components are obtained only in the npm test script( you can see the scripts for testing in package.json), for testing, just run:
+
+```bash
+$ npm install
+$ npm test
+```
+
+## License
+[MIT](LICENSE)

+ 2 - 0
const/index.js

@@ -0,0 +1,2 @@
+
+exports.result = require( './result' );

+ 15 - 0
const/result.js

@@ -0,0 +1,15 @@
+module.exports = {};
+
+function define( key, value ) {
+	module.exports[ key ] = value;
+}
+
+define( 'SUCCESS', 0 );
+
+// Chat room related consts.
+define( 'INVALID_CHATROOMID', 1000 );
+define( 'INVALID_CHATROOMID_MSG', 'Invalid chat room id! Space, tab and new line are not allowed in chat room id!' );
+
+
+define( 'INVALID_GROUPID', 2000 );
+define( 'INVALID_GROUPID_MSG', 'Invalid group id! Space, tab and new line are not allowed in group id!' );

+ 1 - 0
index.js

@@ -0,0 +1 @@
+module.exports = require( './lib' );

+ 55 - 0
lib/apis.js

@@ -0,0 +1,55 @@
+module.exports = {};
+
+function define( key, value ) {
+  module.exports[key] = value;
+}
+
+define( 'user', {
+  'getToken'      : '/user/getToken',
+  'refresh'       : '/user/refresh',
+  'checkOnline'   : '/user/checkOnline',
+
+  'block'         : '/user/block',
+  'unblock'       : '/user/unblock',
+  'queryBlocked'  : '/user/block/query',
+
+  'addToBlackList'      : '/user/blacklist/add',
+  'removeFromBlacklist' : '/user/blacklist/remove',
+  'queryBlacklist'      : '/user/blacklist/query'
+} );
+
+define( 'message', {
+  'private' : {
+    'publish'          : '/message/publish',
+    'publish_template' : '/message/private/publish_template'
+  },
+  'system' : {
+    'publish' : '/message/system/publish'
+  },
+  'group' : {
+    'publish' : '/message/group/publish'
+  },
+  'chatroom' : {
+    'publish' : '/message/chatroom/publish'
+  },
+  'publish'   : '/message/publish',
+  'broadcast' : '/message/broadcast',
+  'history' : '/message/history'
+} );
+
+define( 'group', {
+  'sync'    : '/group/sync',
+  'create'  : '/group/create',
+  'quit'    : '/group/quit',
+  'dismiss' : '/group/dismiss',
+  'refresh' : '/group/refresh'
+} );
+
+define( 'chatroom', {
+  'create'  : '/chatroom/create',
+  'destroy' : '/chatroom/destroy',
+  'query'   : '/chatroom/query',
+  'user'    : {
+    'query' : '/chatroom/user/query'
+  }
+} );

+ 76 - 0
lib/chatroom.js

@@ -0,0 +1,76 @@
+var apis 		= require( './apis' );
+var util 		= require( './util' );
+var rongrequest = require( './rongrequest' );
+var rcResult 	= require( '../const' ).result;
+
+exports.create = function( chatRoomIDNamePairs, format, callback ) {
+	var params = {};
+	var chatRoomId;
+	var validChatRoomId = true;
+	for( var i=0; i<chatRoomIDNamePairs.length; ++i ) {
+		chatRoomId = chatRoomIDNamePairs[i].id;
+		if( !util.validateId( chatRoomId ) ) {
+			validChatRoomId = false;
+			break;
+		}
+		var key = 'chatroom[' + chatRoomId + ']';
+		params[ key ] = chatRoomIDNamePairs[i].name;
+	}
+	if( validChatRoomId ) {
+		rongrequest.request( apis['chatroom']['create'], params, format, callback );	
+	}
+	else {
+		return callback( { code : rcResult.INVALID_CHATROOMID, message : rcResult.INVALID_CHATROOMID_MSG }, null );
+	}
+}
+
+exports.destroy = function( chatRoomIDs, format, callback ) {
+	if( 'object' == typeof chatRoomIDs && chatRoomIDs.length ) {
+		if( !util.validateIDs( chatRoomIDs ) ) {
+			return callback( { code : rcResult.INVALID_CHATROOMID, message : rcResult.INVALID_CHATROOMID_MSG }, null );
+		}
+		rongrequest.requestWithSameFields( apis['chatroom']['destroy'], {}, [{field:'chatroomId', values:chatRoomIDs}], format, callback );
+	}
+	else {
+		if( !util.validateId( chatRoomIDs ) ) {
+			return callback( { code : rcResult.INVALID_CHATROOMID, message : rcResult.INVALID_CHATROOMID_MSG }, null );
+		}
+		rongrequest.request( apis['chatroom']['destroy'], { chatroomId : chatRoomIDs }, format, callback );
+	}
+}
+
+exports.query = function( chatRoomIDs, format, callback ) {
+	if( 'object' == typeof chatRoomIDs && chatRoomIDs.length ) {
+		// Check the IDs.
+		var valid = util.validateIDs( chatRoomIDs );
+		if( !valid ) {
+			callback( { code : rcResult.INVALID_CHATROOMID, message : rcResult.INVALID_CHATROOMID_MSG }, null );
+		}
+		else {
+			rongrequest.requestWithSameFields( apis['chatroom']['query'], {}, [{field:'chatroomId', values:chatRoomIDs}], format, callback );
+		}
+	}
+	else {
+		if( !util.validateId( chatRoomIDs ) ) {
+			return callback( { code : rcResult.INVALID_CHATROOMID, message : rcResult.INVALID_CHATROOMID_MSG }, null );
+		}
+		rongrequest.request( apis['chatroom']['query'], { chatroomId : chatRoomIDs }, format, callback );
+	}
+	
+}
+
+exports.user = {};
+exports.user.query = function( chatRoomId, format, callback ) {
+  if( !util.validateId( chatRoomId ) ) {
+	  return callback( { code : rcResult.INVALID_CHATROOMID, message : rcResult.INVALID_CHATROOMID_MSG }, null );
+  }
+  rongrequest.request( apis['chatroom']['user']['query'], {
+    chatroomId : chatRoomId
+  }, format, function( err, resultText ) {
+    return callback( err, resultText );
+  } );
+}
+
+exports.queryAll = function( format, callback ) {
+	rongrequest.requestWithSameFields( apis['chatroom']['query'], {}, [], format, callback );
+}

+ 93 - 0
lib/group.js

@@ -0,0 +1,93 @@
+var apis 		= require( './apis' );
+var rongrequest = require( './rongrequest' );
+var util 		= require( './util' );
+var rcResult 	= require( '../const' ).result;
+
+exports.sync = function( userId, groupIdNamePairs, format, callback ) {
+	var params = { userId : userId };
+	var validGroupId = true;
+	for( var k in groupIdNamePairs ) {
+		if( !util.validateId( k ) ) {
+			validGroupId = false;
+			break;
+		}
+		params[ 'group[' + k + ']' ] = groupIdNamePairs[k];
+	}
+
+	if( validGroupId ) {
+		rongrequest.request( apis['group']['sync'], params, format, function( err, resultText ) {
+			return callback( err, resultText );
+		} );		
+	}
+	else {
+		return callback( { code : rcResult.INVALID_GROUPID, message : rcResult.INVALID_GROUPID_MSG }, null );
+	}
+}
+
+exports.quit = function( userIDs, groupId, format, callback ) {
+	if( !util.validateId( groupId ) ) {
+		return callback( 'Invalid group id', null );
+	}
+
+	var params = { groupId : groupId };
+	if( typeof userIDs == 'object' && userIDs.length ) {
+		rongrequest.requestWithSameFields( apis['group']['quit'], params, [ { field : 'userId', values : userIDs } ], format, callback );
+	} 
+	else if( typeof userIDs == 'string' || typeof userIDs == 'number' ) {
+		params.userId = userIDs;
+		rongrequest.request( apis['group']['quit'], params, format, callback );
+	}
+	else {
+		return callback( "Invalid userId! The userId should be either a string, a number, or an non-empty array!" );
+	}
+}
+
+exports.dismiss = function( userId, groupId, format, callback ) {
+	if( !util.validateId( groupId ) ) {
+		return callback( { code : rcResult.INVALID_GROUPID, message : rcResult.INVALID_GROUPID_MSG }, null );
+	}
+
+	rongrequest.request( apis['group']['dismiss'], {
+		userId  : userId,
+		groupId : groupId		
+	}, format, function( err, resultText ) {
+		return callback( err, resultText );
+	} );
+}
+
+
+exports.refresh = function( groupId, groupName, format, callback ) {
+	if( !util.validateId( groupId ) ) {
+		return callback( { code : rcResult.INVALID_GROUPID, message : rcResult.INVALID_GROUPID_MSG }, null );
+	}
+
+  rongrequest.request( apis['group']['refresh'], {
+    groupId   : groupId,
+    groupName : groupName
+  }, format, function( err, resultText ) {
+    return callback( err, resultText );
+  } );
+
+}
+
+function createGroup( userIDs, groupId, groupName, format, callback ) {
+	if( !util.validateId( groupId ) ) {
+		return callback( { code : rcResult.INVALID_GROUPID, message : rcResult.INVALID_GROUPID_MSG }, null );
+	}
+
+	var params = { groupId : groupId, groupName : groupName };
+	if( typeof userIDs == 'object' && userIDs.length ) {
+		rongrequest.requestWithSameFields( apis['group']['create'], params, [ { field : 'userId', values : userIDs } ], format, callback );
+	} 
+	else if( typeof userIDs == 'string' || typeof userIDs == 'number' ) {
+		params.userId = userIDs;
+		rongrequest.request( apis['group']['create'], params, format, callback );
+	}
+	else {
+		return callback( "Invalid userId! The userId should be either a string, a number, or an non-empty array!" );
+	}
+}
+
+
+exports.create = createGroup;
+exports.join   = createGroup;

+ 10 - 0
lib/index.js

@@ -0,0 +1,10 @@
+var rongrequest = require( './rongrequest' );
+
+exports.user 	 = require( './user' );
+exports.message  = require( './message' );
+exports.group 	 = require( './group' );
+exports.chatroom = require( './chatroom' );
+
+
+exports.init 			  = rongrequest.init;
+exports.validateSignature = rongrequest.validateSignature;

+ 169 - 0
lib/message.js

@@ -0,0 +1,169 @@
+var apis = require( './apis' );
+var rongrequest = require( './rongrequest' );
+
+exports.private = {};
+
+exports.private.publish = exports.publish = function( fromUserId, toUserId, objectName, content, pushContent, pushData, format, callback ) {
+  var requestParams = {
+		fromUserId : fromUserId,
+		toUserId   : toUserId,
+		objectName : objectName,
+		content    : content
+  }
+
+  var f = arguments[ arguments.length - 2 ];
+  var formatSpecified = f === 'json' || f === 'xml';
+
+  var indexOfLastParam = formatSpecified ? arguments.length - 3 : arguments.length - 2;
+
+  callback = arguments[arguments.length - 1];
+
+  var optionalArgs = { '4' : 'pushContent', '5' : 'pushData' };
+  for( var i=4; i<=indexOfLastParam; ++i ) {
+    if( optionalArgs[i] ) {
+      requestParams[ optionalArgs[i] ] = arguments[i];
+    }
+  }
+
+	rongrequest.request( apis['message']['publish'], requestParams, formatSpecified ? f : 'json', function( err, resultText ) {
+		return callback( err, resultText );
+	} );
+}
+
+exports.private.publish_template = function( fromUserId, toUserIDs, objectName, content, values, pushContent, pushData, format, callback ) {
+  var requestParams = {
+    fromUserId  : fromUserId,
+    objectName  : objectName,
+    content     : content,
+    toUserId    : toUserIDs,
+    values      : values,
+    pushContent : pushContent,
+    pushData    : pushData
+  }
+
+  rongrequest.requestWithHeaders( apis[ 'message']['private']['publish_template'], requestParams, format, { 'Content-Type' : 'application/json' }, function( err, resultText ) {
+    return callback( err, resultText );
+  } );
+}
+
+exports.system = {};
+
+exports.system.publish = function( fromUserId, toUserIDs, objectName, content, pushContent, pushData, format, callback ) {
+  var requestParams = {
+    fromUserId  : fromUserId,
+    objectName  : objectName,
+    content     : content
+  }
+
+  var f = arguments[ arguments.length - 2 ];
+  var formatSpecified = f === 'json' || f === 'xml';
+
+  var indexOfLastParam = formatSpecified ? arguments.length - 3 : arguments.length - 2;
+
+  callback = arguments[arguments.length - 1];
+
+  var optionalArgs = { '4' : 'pushContent', '5' : 'pushData' };
+  for( var i=4; i<=indexOfLastParam; ++i ) {
+    if( optionalArgs[i] ) {
+      requestParams[ optionalArgs[i] ] = arguments[i];
+    }
+  }
+
+  rongrequest.requestWithSameFields( apis[ 'message']['system']['publish'], requestParams, [ { field : 'toUserId', values : toUserIDs } ], formatSpecified ? f : 'json', function( err, resultText ) {
+    return callback( err, resultText );
+  } );
+}
+
+exports.group = {};
+exports.group.publish = function( fromUserId, toGroupId, objectName, content, pushContent, pushData, format, callback ) {
+  var requestParams = {
+    fromUserId  : fromUserId,
+    toGroupId   : toGroupId,
+    objectName  : objectName,
+    content     : content
+  }
+
+  var f = arguments[ arguments.length - 2 ];
+  var formatSpecified = f === 'json' || f === 'xml';
+
+  var indexOfLastParam = formatSpecified ? arguments.length - 3 : arguments.length - 2;
+
+  callback = arguments[arguments.length - 1];
+
+  var optionalArgs = { '4' : 'pushContent', '5' : 'pushData' };
+  for( var i=4; i<=indexOfLastParam; ++i ) {
+    if( optionalArgs[i] ) {
+      requestParams[ optionalArgs[i] ] = arguments[i];
+    }
+  }
+
+  rongrequest.request( apis[ 'message']['group']['publish'], requestParams, formatSpecified ? f : 'json', function( err, resultText ) {
+    return callback( err, resultText );
+  } );
+}
+
+exports.chatroom = {};
+exports.chatroom.publish = function( fromUserId, toChatroomId, objectName, content, pushContent, pushData, format, callback ) {
+  var requestParams = {
+    fromUserId   : fromUserId,
+    toChatroomId : toChatroomId,
+    objectName   : objectName,
+    content      : content
+  }
+
+  var f = arguments[ arguments.length - 2 ];
+  var formatSpecified = f === 'json' || f === 'xml';
+
+  var indexOfLastParam = formatSpecified ? arguments.length - 3 : arguments.length - 2;
+
+  callback = arguments[arguments.length - 1];
+
+  var optionalArgs = { '4' : 'pushContent', '5' : 'pushData' };
+  for( var i=4; i<=indexOfLastParam; ++i ) {
+    if( optionalArgs[i] ) {
+      requestParams[ optionalArgs[i] ] = arguments[i];
+    }
+  }
+
+  rongrequest.request( apis[ 'message']['chatroom']['publish'], requestParams, formatSpecified ? f : 'json', function( err, resultText ) {
+    return callback( err, resultText );
+  } );
+}
+
+/**
+ * 
+ */
+exports.broadcast = function( fromUserId, objectName, content, pushContent, pushData, format, callback ) {
+  var requestParams = {
+		fromUserId  : fromUserId,
+		objectName  : objectName,
+		content     : content
+	}
+
+  var f = arguments[ arguments.length - 2 ];
+  var formatSpecified = f === 'json' || f === 'xml';
+
+  var indexOfLastParam = formatSpecified ? arguments.length - 3 : arguments.length - 2;
+
+  callback = arguments[arguments.length - 1];
+
+  var optionalArgs = { '3' : 'pushContent', '4' : 'pushData' };
+  for( var i=4; i<=indexOfLastParam; ++i ) {
+    if( optionalArgs[i] ) {
+      requestParams[ optionalArgs[i] ] = arguments[i];
+    }
+  }
+
+	rongrequest.request( apis['message']['broadcast'], requestParams, formatSpecified ? f : 'json', function( err, resultText ) {
+		return callback( err, resultText );
+	} );
+}
+
+exports.history = function( dateString, format, callback ) {
+	rongrequest.request( apis['message']['history'], {
+    date : dateString
+	}, format, function( err, resultText ) {
+		return callback( err, resultText );
+	} );
+}
+

+ 126 - 0
lib/rongrequest.js

@@ -0,0 +1,126 @@
+
+var superagent = require( 'superagent' );
+// require('superagent-proxy')(superagent);
+var sha1	   = require( 'sha1' );
+
+var BASEURL	  = 'https://api.cn.rong.io';
+var APPKEY 	  = undefined;
+var APPSECRET = undefined;
+var TIMESTAMP = undefined;
+var SIGNATURE = undefined;
+var NONCE 	  = undefined;
+var HEADERS	  = {};
+
+var FORMAT 	  = 'json';
+// var proxy = process.env.http_proxy
+
+exports.init = function( appKey, appSecret ) {
+	APPKEY 	  = appKey;
+	APPSECRET = appSecret;
+	NONCE	    = parseInt( Math.random() * 0xffffff );
+	TIMESTAMP = Date.parse( new Date() )/1000;
+	SIGNATURE = sha1( APPSECRET + NONCE + TIMESTAMP  );
+
+	HEADERS['App-Key'] 	 	= APPKEY;
+	HEADERS['Nonce'] 		= NONCE;
+	HEADERS['Timestamp']	= TIMESTAMP;
+	HEADERS['Signature']	= SIGNATURE;
+
+	HEADERS['Content-Type'] = 'application/x-www-form-urlencoded';	
+}
+
+
+exports.request = function( api, params, format, callback ) {
+	var f = 'json';
+	if( (typeof format ) == 'function' ) { // Use default format 'json'
+		callback = format;
+	}
+	else {
+		f = ( format === 'xml' ? 'xml' : 'json' );
+	}
+
+
+	superagent.agent()
+	.post( BASEURL + api + '.' + f )
+	.set( HEADERS )
+	.send( params )
+	.end( function( err, result ) {
+		if( err ) {
+			return callback( err, null );
+		}
+		else {
+			return callback( null, result.text );
+		}
+	} );
+}
+
+
+exports.requestWithSameFields = function( api, params, fieldAndValues, format, callback ) {
+	var f = 'json';
+	if( (typeof format ) == 'function' ) { // Use default format 'json'
+		callback = format;
+	}
+	else {
+		f = ( format === 'xml' ? 'xml' : 'json' );
+	}
+
+	var agent = superagent.agent()
+	.post( BASEURL + api + '.' + f )
+  	// .proxy(proxy)
+	.set( HEADERS );
+
+	for( var i=0; i<fieldAndValues.length; ++i ) {
+		var field = fieldAndValues[i].field;
+		var values = fieldAndValues[i].values;
+		for( var j=0; j<values.length; ++j ) {
+			agent = agent.send( encodeURIComponent(field) + '=' + encodeURIComponent(values[j]) );
+		}
+	}
+
+  for( var k in params ) {
+    agent = agent.send( encodeURIComponent(k) + '=' + encodeURIComponent(params[k]) );
+  }
+
+  agent.end( function( err, result ) {
+		if( err ) {
+			return callback( err, null );
+		}
+		else {
+			return callback( null, result.text );
+		}
+	} );
+}
+
+exports.validateSignature = function( nonce, timestamp, signature ) {
+	return sha1( APPSECRET + nonce + timestamp ) == signature;
+}
+
+
+exports.requestWithHeaders = function( api, params, format, headers, callback ) {
+	var f = 'json';
+	if( (typeof format ) == 'function' ) { // Use default format 'json'
+		callback = format;
+	}
+	else {
+		f = ( format === 'xml' ? 'xml' : 'json' );
+	}
+
+  // Attach the specified headers.
+  var _headers = {};
+  for( var k in HEADERS ) {
+    _headers[k] = headers[k] ? headers[k] : HEADERS[k];
+  }
+
+	superagent.agent()
+	.post( BASEURL + api + '.' + f )
+	.set( _headers )
+	.send( params )
+	.end( function( err, result ) {
+		if( err ) {
+			return callback( err, null );
+		}
+		else {
+			return callback( null, result.text );
+		}
+	} );
+}

+ 95 - 0
lib/user.js

@@ -0,0 +1,95 @@
+var apis = require( './apis' );
+var rongrequest = require( './rongrequest' );
+
+exports.getToken = function( userId, name, portraitUri, format, callback ) {
+	rongrequest.request( apis['user']['getToken'], {
+		userId 		: userId,
+		name   		: name,
+		portraitUri : portraitUri
+	}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+exports.refresh = function( userId, name, portraitUri, format, callback ) {
+	rongrequest.request( apis['user']['refresh'], {
+		userId 		: userId,
+		name   		: name,
+		portraitUri : portraitUri
+	}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+
+exports.checkOnline = function( userId, format, callback ) {
+	rongrequest.request( apis['user']['checkOnline'], {
+		userId 		: userId
+	}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+
+exports.block = function( userId, numMinutes, format, callback ) {
+  numMinutes = parseInt( numMinutes );
+
+  if( numMinutes <= 0 ) { // Don't need to call the API.
+    return callback( null, '{"code":200}' );
+  }
+
+  if( numMinutes > 43200 ) {
+    numMinutes = 43200;
+  }
+
+	rongrequest.request( apis['user']['block'], {
+		userId 		: userId,
+    minute    : numMinutes
+	}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+
+
+exports.unblock = function( userId, format, callback ) {
+	rongrequest.request( apis['user']['unblock'], {
+		userId 		: userId
+	}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+exports.block.query = function( format, callback ) {
+	rongrequest.request( apis['user']['queryBlocked'], {}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+exports.blacklist = {};
+
+exports.blacklist.add = function( userId, blackUserId, format, callback ) {
+	rongrequest.request( apis['user']['addToBlackList'], {
+		userId 		  : userId,
+    blackUserId : blackUserId
+	}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+exports.blacklist.remove = function( userId, blackUserId, format, callback ) {
+	rongrequest.request( apis['user']['removeFromBlacklist'], {
+		userId 		  : userId,
+    blackUserId : blackUserId
+	}, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}
+
+exports.blacklist.query = function( userId, format, callback ) {
+	rongrequest.request( apis['user']['queryBlacklist'], {
+    userId : userId
+  }, format, function( err, result ) {
+		return callback( err, result );
+	} );
+}

+ 18 - 0
lib/util.js

@@ -0,0 +1,18 @@
+
+
+function validateId( id ) {
+	return ( id.indexOf( ' ' ) == -1 && id.indexOf( '\t' ) == -1 && id.indexOf( '\n' ) == -1 && id.indexOf( '\r' ) == -1 && id.indexOf( '\r\n' ) == -1  );
+}
+
+exports.validateIDs = function( IDs ) {
+	var valid = true;
+	for( var i=0; i<IDs.length; ++i ) {
+		if( ! validateId( IDs[i] ) ) {
+			valid = false;
+			break;
+		}
+	}
+	return valid;
+}
+
+exports.validateId = validateId;

+ 94 - 0
package.json

@@ -0,0 +1,94 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "rongcloud-sdk@1.0.3",
+        "scope": null,
+        "escapedName": "rongcloud-sdk",
+        "name": "rongcloud-sdk",
+        "rawSpec": "1.0.3",
+        "spec": "1.0.3",
+        "type": "version"
+      },
+      "/home/ke/wd/ts/imsrv"
+    ]
+  ],
+  "_from": "rongcloud-sdk@1.0.3",
+  "_id": "rongcloud-sdk@1.0.3",
+  "_inCache": true,
+  "_location": "/rongcloud-sdk",
+  "_nodeVersion": "0.12.4",
+  "_npmUser": {
+    "name": "crystaldust",
+    "email": "juzhenatpku@gmail.com"
+  },
+  "_npmVersion": "2.10.1",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "rongcloud-sdk@1.0.3",
+    "scope": null,
+    "escapedName": "rongcloud-sdk",
+    "name": "rongcloud-sdk",
+    "rawSpec": "1.0.3",
+    "spec": "1.0.3",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "#USER"
+  ],
+  "_resolved": "https://registry.npmjs.org/rongcloud-sdk/-/rongcloud-sdk-1.0.3.tgz",
+  "_shasum": "188efe8e5e8db3ca627628f1702e91d35c4da9ad",
+  "_shrinkwrap": null,
+  "_spec": "rongcloud-sdk@1.0.3",
+  "_where": "/home/ke/wd/ts/imsrv",
+  "author": {
+    "name": "RongCloud"
+  },
+  "bugs": {
+    "url": "https://github.com/rongcloud/server-sdk-nodejs/issues"
+  },
+  "contributors": [
+    {
+      "name": "crystaldust",
+      "email": "juzhenatpku@gmail.com"
+    }
+  ],
+  "dependencies": {
+    "sha1": ">=1.1.0",
+    "superagent": ">=0.21.0"
+  },
+  "description": "The Server SDK of RongCloud",
+  "devDependencies": {},
+  "directories": {},
+  "dist": {
+    "shasum": "188efe8e5e8db3ca627628f1702e91d35c4da9ad",
+    "tarball": "https://registry.npmjs.org/rongcloud-sdk/-/rongcloud-sdk-1.0.3.tgz"
+  },
+  "gitHead": "cc1762dabd6ae59e1682ded62d4b3926f0024923",
+  "homepage": "http://rongcloud.cn",
+  "keywords": [
+    "mobile",
+    "im",
+    "server",
+    "sdk"
+  ],
+  "license": "MIT",
+  "main": "index.js",
+  "maintainers": [
+    {
+      "name": "crystaldust",
+      "email": "juzhenatpku@gmail.com"
+    }
+  ],
+  "name": "rongcloud-sdk",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/rongcloud/server-sdk-nodejs.git"
+  },
+  "scripts": {
+    "test": "cnpm install async@1.2.1 mocha@2.2.5 should@6.0.3 underscore@1.8.3 xml2js@0.4.9 && cd test && mocha ."
+  },
+  "version": "1.0.3"
+}

TEMPAT SAMPAH
test/cn_lesson1_1.amr


+ 21 - 0
test/config.js

@@ -0,0 +1,21 @@
+module.exports = {
+	appKey 		: 'mgb7ka1nbs3wg',
+	appSecret 	: 'm1Rv2MHHND',
+	token : {
+		userId 		: '0001',
+		name   		: 'TestUser',
+		portraitUri : 'http://rongcloud.cn/images/logo.png'
+	},
+	message : {
+		fromUserId  : '546eb521c613156d331e91bb',
+		toUserId	: '5460603c1002a6e311f89e7f',
+		textMsg		: 'Hello, world!'
+	},
+	group : {
+		userId : '5460603c1002a6e311f89e7f',
+		groupIdNamePairs : { 'ProgrammerGroup1' : '程序猿交流群1', 'DriverGroup1' : '赛车手爱好者2群' }
+	},
+	chatroom : {
+		chatroomIdNamePairs : { 'EatingFans' : '吃货大本营', 'ProCylcing' : '骑行专家', 'DriodGeek' : '手机极客', 'HackerHome' : '黑客之家' }
+	}
+}

TEMPAT SAMPAH
test/raindrop.jpg


+ 87 - 0
test/test.chatroom.js

@@ -0,0 +1,87 @@
+var should 		= require( 'should' );
+var _ 			= require( 'underscore' );
+
+var testConfig  = require( './config' );
+var rongSDK 	= require( '../index' );
+
+var chatroomIDs   = _.keys( testConfig.chatroom.chatroomIdNamePairs );
+var chatroomNames = _.values( testConfig.chatroom.chatroomIdNamePairs );
+
+describe( 'Chatroom Test', function() {
+	before( function( done ) {
+		// Init the SDK before testing.
+		rongSDK.init( testConfig.appKey, testConfig.appSecret );
+		done();
+	} );
+
+	after( function( done ) {
+		done();
+	} );
+
+	describe( 'Create Chatroom', function() {
+		it( 'Create chatroom: should return OK', function( done ) {
+			var chatroomIdNamePairsArray = [];
+			_.each( chatroomIDs, function( chatroomId ) {
+				chatroomIdNamePairsArray.push( { id : chatroomId, name : testConfig.chatroom.chatroomIdNamePairs[ chatroomId ] } );
+			} );
+			rongSDK.chatroom.create( chatroomIdNamePairsArray , function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();				
+			} );
+		} );
+	} );
+
+	describe( 'Destroy Chatroom', function() {
+		it( 'Destroy a single chatroom: should return OK', function( done ) {
+			rongSDK.chatroom.destroy( chatroomIDs.pop(), function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+
+		it( 'Destroy chatrooms: should return OK', function( done ) {
+			rongSDK.chatroom.destroy( chatroomIDs.splice( 0, 2 ), function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+
+	} );
+
+
+	describe( 'Query Chatroom', function() {
+		it( 'Query a single chatroom: should return OK', function( done ) {
+			rongSDK.chatroom.query( chatroomIDs[0], function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				var found = _.findWhere( result.chatRooms, { chrmId : chatroomIDs[0] } );
+				found.should.not.be.undefined;
+				found.should.have.property( 'chrmId', chatroomIDs[0] );				
+				done();
+			} );
+		} );
+
+	} );
+
+	describe( 'Query Chatroom Users', function() {
+		it( 'Query the users of a chatroom: should return OK', function( done ) {
+			rongSDK.chatroom.user.query( chatroomIDs[0], function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+
+	} );
+
+
+
+} );

+ 107 - 0
test/test.group.js

@@ -0,0 +1,107 @@
+var should 		= require( 'should' );
+var _ 			= require( 'underscore' );
+
+var testConfig  = require( './config' );
+var rongSDK 	= require( '../index' );
+
+var groupIDs 	= _.keys( testConfig.group.groupIdNamePairs );
+var groupNames 	= _.values( testConfig.group.groupIdNamePairs );
+
+describe( 'Group Test', function() {
+	before( function( done ) {
+		// Init the SDK before testing.
+		rongSDK.init( testConfig.appKey, testConfig.appSecret );
+		done();
+	} );
+
+	after( function( done ) {
+		done();
+	} );
+
+	describe( 'Sync Group', function() {
+		it( 'Synchornize group: should return OK', function( done ) {
+			rongSDK.group.sync( testConfig.group.userId, testConfig.group.groupIdNamePairs, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+	} );
+
+	describe( 'Quit Group', function() {
+		it( 'Quit group: should return OK', function( done ) {
+			rongSDK.group.quit( testConfig.group.userId, groupIDs[0], function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+	} );
+
+	describe( 'Create/Join Group', function() {
+		it( 'Create/Join group with a single user: should return OK', function( done ) {
+			rongSDK.group.create( testConfig.group.userId, 'MyGroupID', 'MyGroupName' , function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+
+		it( 'Create/Join group with multiple users: should return OK', function( done ) {
+			rongSDK.group.join( [ testConfig.message.fromUserId, testConfig.message.toUserId ], 'MyGroupID', 'MyGroupName' , function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+	} );
+
+	describe( 'Dissmiss Group', function() {
+		it( 'Dismiss group(' + groupNames[0] + '): should return OK', function( done ) {
+			rongSDK.group.dismiss( testConfig.group.userId, groupIDs[0], function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+
+		it( 'Dismiss group(' + groupNames[1] + '): should return OK', function( done ) {
+			rongSDK.group.dismiss( testConfig.group.userId, groupIDs[1], function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+
+		it( 'Dismiss group(MyGroupName): should return OK', function( done ) {
+			rongSDK.group.dismiss( testConfig.group.userId, 'MyGroupID', function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+
+	} );
+
+
+	describe( 'Refresh Group', function() {
+    var newGroupName = groupNames[0];
+		it( 'Refresh group(' + groupNames[0] + ') to be ' + newGroupName + ': should return OK', function( done ) {
+			rongSDK.group.refresh( groupIDs[0], newGroupName, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+  });
+
+
+} );

+ 410 - 0
test/test.message.js

@@ -0,0 +1,410 @@
+var async       = require( 'async' );
+var parseString = require( 'xml2js' ).parseString;
+var fs 			    = require( 'fs' );
+var should 		  = require( 'should' );
+
+var testConfig  = require( './config' );
+var rongSDK 	  = require( '../index' );
+
+var base64Voice	= new Buffer( fs.readFileSync( './testvoice.amr' ) ).toString( 'base64' );
+var base64Image	= new Buffer( fs.readFileSync( './raindrop.jpg' ) ).toString( 'base64' );
+
+var optionalArgs = [ 'push content', 'push data' ];
+
+describe( 'Message Test', function() {
+	before( function( done ) {
+		// Init the SDK before testing.
+		rongSDK.init( testConfig.appKey, testConfig.appSecret );
+		done();
+	} );
+
+	after( function( done ) {
+		done();
+	} );
+
+	describe( 'Publish Message', function() {
+		it( 'Text message: should return OK', function( done ) {
+			var textMessageObject = { content : testConfig.message.textMsg };
+      // Testing for all the situations of arguments.
+      var args = [ testConfig.message.fromUserId, testConfig.message.toUserId, 'RC:TxtMsg', JSON.stringify( textMessageObject ) ];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+		} );
+
+		it( 'Image message: should return OK', function( done ) {
+			var imageMessageObject = { content : base64Image, imageUrl : 'http://lanceju-com.qiniudn.com/raindrop.jpg' };
+			var args = [ testConfig.message.fromUserId, testConfig.message.toUserId, 'RC:ImgMsg', JSON.stringify( imageMessageObject ) ];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+
+		} );
+
+		it( 'Voice message: should return OK', function( done ) {
+
+			var voiceMessageObject = { content : base64Voice, duration : 4 };
+			var args = [testConfig.message.fromUserId, testConfig.message.toUserId, 'RC:VcMsg', JSON.stringify( voiceMessageObject ) ];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+		} );
+
+
+		it( 'Image Text message: should return OK', function( done ) {
+			var imageTextMessageObject = { title : 'hellotitle', content : 'hello', imageUrl : 'http://lanceju-com.qiniudn.com/raindrop.jpg', extra : 'image from a user' };
+
+			var args = [testConfig.message.fromUserId, testConfig.message.toUserId, 'RC:ImgTextMsg', JSON.stringify( imageTextMessageObject ) ];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+		} );
+
+		it( 'Location message: should return OK', function( done ) {
+			var locationMessageObject = { content : 'You got a location message', latitude : 24.114, longitude : 334.221, poi : '北京市朝阳区北苑路北辰泰岳大厦', extra : 'The location of rong cloud' };
+
+			var args = [testConfig.message.fromUserId, testConfig.message.toUserId, 'RC:LBSMsg', JSON.stringify( locationMessageObject ) ];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.private.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+		} );
+
+		it( 'ContactNtf message: should return OK', function( done ) {
+			var contactNtfMessageObject = { operation:"op1",sourceUserId:"24",targetUserId:"21",message:"haha",extra:"helloExtra"};
+			var args = [testConfig.message.fromUserId, testConfig.message.toUserId, 'RC:ContactNtf', JSON.stringify( contactNtfMessageObject ) ];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.private.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+		} );
+
+	} );
+
+
+	describe( 'Publish Template Message', function() {
+		it( 'Send template message: should return OK', function( done ) {
+
+      var content = JSON.stringify( { content : "aa{c}{e}{d}", extra : "bb" } );
+      var values  = [ { "{c}":"1","{d}":"2","{e}":"3"} ];
+
+      /*
+			rongSDK.message.private.publish_template( testConfig.message.fromUserId, [testConfig.message.toUserId], 'RC:TxtMsg', content, values, [ 'push content for user' ], ['push data for user'], function( err, resultText ) {
+        return done();
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+      */
+
+			var args = [testConfig.message.fromUserId, [testConfig.message.toUserId], 'RC:TxtMsg', content, values, [ 'push content for user' ], ['push data for user'] ];
+
+      var argsArray = [];
+      argsArray.push( args.concat() );
+      argsArray.push( args.concat( 'json' ) );
+      argsArray.push( args.concat( 'xml' ) );
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.private.publish_template.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+
+
+		} );
+
+		it( 'Send system message: should return OK', function( done ) {
+
+			var args = [testConfig.message.fromUserId, [testConfig.message.toUserId], 'RC:TxtMsg', JSON.stringify( { content : 'Hello, world!' } )];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.system.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+
+		} );
+
+    it( 'Send group message before joining the group: should return error', function( done ) {
+      rongSDK.message.group.publish( testConfig.message.fromUserId, 'ProgrammerGroup1', 'RC:TxtMsg', JSON.stringify( { content : 'Hello, world!' } ), null, null, function( err, resultText ) {
+        should.exists( err );
+        // Joining the group
+        rongSDK.group.join( testConfig.message.fromUserId, 'ProgrammerGroup1', 'Programmers group', function( err, resultText ) {
+          should.not.exists( err );
+          var result = JSON.parse( resultText );
+          result.code.should.equal( 200 );
+          done();
+        } );
+
+      } );
+    } );
+
+    it( 'Send group message after joining the group: should return OK', function( done ) {
+      var args = [testConfig.message.fromUserId, 'ProgrammerGroup1', 'RC:TxtMsg', JSON.stringify( { content : 'Hello, world!' } )];
+
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.group.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        // Dismiss the group after testing.
+        rongSDK.group.quit( testConfig.message.fromUserId, 'ProgrammerGroup1', function( err, resultText ) {
+          should.not.exists( err );
+          var result = JSON.parse( resultText );
+          result.code.should.equal( 200 );
+          done();
+        } );
+      } );
+
+    } );
+
+		it( 'Send chatroom message: should return OK', function( done ) {
+			var args = [testConfig.message.fromUserId, 'my chatroom', 'RC:TxtMsg', JSON.stringify( { content : 'Hello, world!' } ) ];
+      var argsArray = [];
+      for( var i=0; i<3; ++i ) {
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ) ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'json' ) );
+        argsArray.push( args.concat( optionalArgs.slice( 0, i ), 'xml' ) );
+      }
+
+      async.each( argsArray, function( _args, cb ) {
+        rongSDK.message.chatroom.publish.apply( this, _args.concat( function( err, resultText ) {
+          if( _args[ _args.length - 1 ] === 'xml' ) {
+            should.not.exists( err );
+            parseString( resultText, function( err, result ) {
+              parseInt( result.xml.code[0] ).should.equal( 200 );
+              cb();
+            } );
+          }
+          else {
+            should.not.exists( err );
+            var result = JSON.parse( resultText );
+            result.code.should.equal( 200 );
+            cb();
+          }
+        } ) );
+      }, function() {
+        done();
+      } );
+		} );
+
+  } );
+
+
+  // Since this API is a charing service, make sure your appKey&appSecret is from an charged account.
+	// describe( 'Broadcast Message', function() {
+	// 	it( 'Should return OK', function( done ) {
+	// 		rongSDK.message.broadcast( testConfig.message.fromUserId, 'RC:TxtMsg', testConfig.message.textMsg, function( err, resultText ) {
+	// 			should.not.exists( err );
+	// 			var result = JSON.parse( resultText );
+	// 			result.code.should.equal( 200 );
+	// 			done();
+	// 		} );
+	// 	} );
+	// } );
+
+} );

+ 177 - 0
test/test.user.js

@@ -0,0 +1,177 @@
+
+var should 		= require( 'should' );
+var testConfig  = require( './config' );
+var rongSDK 	= require( '../index' );
+
+
+describe( 'User Test', function() {
+	before( function( done ) {
+		// Init the SDK before testing.
+		rongSDK.init( testConfig.appKey, testConfig.appSecret );
+		done();
+	} );
+
+	after( function( done ) {
+		done();
+	} );
+
+	describe( 'Get token', function() {
+		it( 'Should return OK', function( done ) {
+			rongSDK.user.getToken( testConfig.token.userId, testConfig.token.name, testConfig.token.portraitUri, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+	} );
+
+
+
+	describe( 'Refresh user info', function() {
+		it( 'Should return OK', function( done ) {
+			rongSDK.user.refresh( testConfig.token.userId, testConfig.token.name, testConfig.token.portraitUri, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+				done();
+			} );
+		} );
+	} );
+
+	describe( 'Check user online status', function() {
+		it( 'Check a non-existing user, should return OK', function( done ) {
+			rongSDK.user.checkOnline( 'im not here', function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+        result.status.should.equal( '0' );  // Notice: the status is a string, not int.
+				done();
+			} );
+		} );
+	} );
+
+	describe( 'Block user', function() {
+		it( 'Block a user within 1 minute, should return OK', function( done ) {
+			rongSDK.user.block( testConfig.token.userId, 1, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+
+        // Check if the user is in the blocked list.
+			  rongSDK.user.block.query ( function( err, resultText ) {
+          should.not.exists( err );
+          var result = JSON.parse( resultText );
+          result.code.should.equal( 200 );
+          should.exists( result.users );
+          var isUserBlocked = findUser( result.users, testConfig.token.userId );
+          isUserBlocked.should.equal( true );
+				  done();
+        } );
+
+			} );
+		} );
+	} );
+
+
+	describe( 'Unblock user', function() {
+		it( 'Unblock the previously blocked user, should return OK', function( done ) {
+			rongSDK.user.unblock( testConfig.token.userId, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+
+        // Check if the user is in the blocked list.
+			  rongSDK.user.block.query( function( err, resultText ) {
+          should.not.exists( err );
+          var result = JSON.parse( resultText );
+          result.code.should.equal( 200 );
+          should.exists( result.users );
+          var isUserBlocked = findUser( result.users, testConfig.token.userId );
+          isUserBlocked.should.equal( false );
+				  done();
+        } );
+
+			} );
+		} );
+	} );
+
+
+	describe( 'Query blocked users', function() {
+		it( 'The test is obtained in the block/unblock user API tests', function( done ) {
+      done();
+		} );
+	} );
+
+	describe( 'Black list', function() {
+		it( 'Add a user to the black list, should return OK', function( done ) {
+			rongSDK.user.blacklist.add( testConfig.message.toUserId, testConfig.message.fromUserId, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+        done();
+			} );
+		} );
+
+
+    // TODO Send a message from fromUserId to toUserId
+
+		it( 'Query a user\'s black list, should get the "Black User"', function( done ) {
+			rongSDK.user.blacklist.query( testConfig.message.toUserId, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+        var isInBlacklist = findUserId( result.users, testConfig.message.fromUserId );
+        isInBlacklist.should.equal( true );
+				result.code.should.equal( 200 );
+        done();
+			} );
+		} );
+
+		it( 'Remove a user from the black list, should return OK', function( done ) {
+			rongSDK.user.blacklist.remove( testConfig.message.toUserId, testConfig.message.fromUserId, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+				result.code.should.equal( 200 );
+        done();
+			} );
+		} );
+
+    // TODO Send a message from fromUserId to toUserId
+
+		it( 'Query a user\'s black list, should get not the "Black User"', function( done ) {
+			rongSDK.user.blacklist.query( testConfig.message.toUserId, function( err, resultText ) {
+				should.not.exists( err );
+				var result = JSON.parse( resultText );
+        var isInBlacklist = findUserId( result.users, testConfig.message.fromUserId );
+        isInBlacklist.should.equal( false );
+				result.code.should.equal( 200 );
+        done();
+			} );
+		} );
+
+	} );
+
+} );
+
+
+function findUser( users, userId ) {
+  var found = false;
+  for( var i=0; i<users.length; ++i ) {
+    if( users[i].userId === userId ) {
+      found = true;
+      break;
+    }
+  }
+  return found;
+}
+
+function findUserId( userIDs, userId ) {
+  var found = false;
+  for( var i=0; i<userIDs.length; ++i ) {
+    if( userIDs[i] === userId ) {
+      found = true;
+      break;
+    }
+  }
+  return found;
+}

TEMPAT SAMPAH
test/testvoice.amr


TEMPAT SAMPAH
test/testvoice.mp3