rong_cloud_server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c0a792a26e83b1a5420aa834459d44f62be0ec8c
4
+ data.tar.gz: 50b3157736cff25fa9937cd99a002d7fa5a62134
5
+ SHA512:
6
+ metadata.gz: 9a77797344c9ad396fc0f8b8586d68c061944abac3932cac51445470c2f21d28d4e9544a4ec8524bf21899d2c50a24703b51005ac3b8257b7edf4bf61fdd1ad1
7
+ data.tar.gz: 72ae4a709051e62499d409c103feb9c106e6f4d1f49b4cbd30cde0f506c3f8ee4d10c4cde46b9495e9f9fe89386ecd99be40e1372b15d1d29c7c45e678cf88fa
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ /config.yml
2
+ .byebug_history
3
+ rong_cloud.log
4
+ /doc
5
+ .yardoc/
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ 融云 Server SDK
2
+ ===
3
+
4
+ 此 gem 实现了[融云 Server API](http://www.rongcloud.cn/docs/server.html)的大部分接口的 Ruby 实现。
5
+
6
+ ### Getting Started
7
+ 1. 安装此 gem;
8
+ 2. 在项目中添加配置:
9
+
10
+ ```ruby
11
+ RongCloud.configure do |config|
12
+ config.app_key = "APP_KEY"
13
+ config.secret_key = "SECRET_KEY"
14
+ config.host = "http://api.cn.ronghub.com" # default: https://api.cn.ronghub.com, use http is convenient for debugging
15
+ end
16
+ ```
17
+ 3. 通过 service 对象使用:
18
+
19
+ ```ruby
20
+ service = RongCloud::Service.new
21
+
22
+ # 更多方法,请查看测试用例 https://github.com/Martin91/rong_cloud/tree/master/test/rong_cloud/services
23
+ service.get_token(..., ..., ...)
24
+ ```
25
+
26
+ ### 特点
27
+ 1. **轻量**:无其他依赖;
28
+ 2. **简洁**:不过分封装,仅做必要的请求实现,使用方自行处理响应的 JSON 解析后的 Hash 对象,各字段释义请自行查阅融云文档;
29
+ 3. **丰富的异常类型**:针对不同的 HTTP 状态码,抛出相对应的异常类型,同时可以通过异常对象的 `business_code` 方法获取错误业务码,可以参考[request test 的代码](https://github.com/Martin91/rong_cloud/blob/master/test/rong_cloud/request_test.rb)。
30
+
31
+ ### TODOs
32
+ 1. 私聊与系统模板消息;
33
+ 2. 聊天室其他服务以及高级接口实现;
34
+ 3. 实时消息路由;
35
+ 4. 消息历史记录;
36
+ 5. 在线状态订阅。
37
+
38
+ ### How to contribute
39
+ 1. Fork this repo;
40
+ 2. Write your code and test;
41
+ 3. Open a new Pull Request.
@@ -0,0 +1,3 @@
1
+ app_key: APP_KEY
2
+ app_secret: APP_SECRET
3
+ host: https://api.cn.ronghub.com
@@ -0,0 +1,30 @@
1
+ require "logger"
2
+
3
+ module RongCloud
4
+ # 管理融云 SDK 连接配置信息
5
+ #
6
+ module Configuration
7
+ # 默认 API 服务器,使用 https
8
+ DEFAULT_HOST = "https://api.cn.ronghub.com".freeze
9
+
10
+ module ModuleMethods
11
+ attr_accessor :app_key, :app_secret, :host, :logger
12
+
13
+ # 获取目标主机,默认为 https://api.cn.ronghub.com
14
+ #
15
+ def host
16
+ @host || DEFAULT_HOST
17
+ end
18
+
19
+ # 获取日志文件对象,默认日志文件为标准输出
20
+ def logger
21
+ @logger || default_logger
22
+ end
23
+
24
+ def default_logger
25
+ @default_logger ||= ::Logger.new(STDOUT)
26
+ end
27
+ end
28
+ extend ModuleMethods
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ module RongCloud
2
+ # 不支持的消息类型错误
3
+ class UnsupportedMessageChannelName < ::StandardError;end
4
+ # 与融云接口请求相关的基本错误类型,其他错误类型继承此类型
5
+ class RequestError < ::StandardError
6
+ # @!attribute [rw] business_code
7
+ # 接口错误时的业务返回码,详见:http://www.rongcloud.cn/docs/server.html#API_方法返回值说明
8
+ attr_accessor :business_code
9
+ end
10
+
11
+ # 错误请求
12
+ class BadRequest < RequestError;end
13
+ # 验证错误
14
+ class AuthenticationFailed < RequestError;end
15
+ # 被拒绝
16
+ class RequestForbidden < RequestError;end
17
+ # 资源不存在
18
+ class ResourceNotFound < RequestError;end
19
+ # 群上限
20
+ class ExceedLimit < RequestError;end
21
+ # 过多的请求
22
+ class TooManyRequests < RequestError;end
23
+ # 内部服务器错误
24
+ class InternalServerError < RequestError;end
25
+ # 内部服务器响应超时
26
+ class Timeout < RequestError;end
27
+
28
+ # 融云服务器响应状态码到 Ruby 错误类型的映射
29
+ HTTP_CODE_TO_ERRORS_MAP = {
30
+ "400" => BadRequest,
31
+ "401" => AuthenticationFailed,
32
+ "403" => RequestForbidden,
33
+ "404" => ResourceNotFound,
34
+ "405" => ExceedLimit,
35
+ "429" => TooManyRequests,
36
+ "500" => InternalServerError,
37
+ "504" => Timeout
38
+ }.freeze
39
+ end
@@ -0,0 +1,69 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'rong_cloud/signature'
4
+
5
+ module RongCloud
6
+ # 请求封装类,所有请求基于 Net::HTTP,自动支持 http 或者 https
7
+ #
8
+ module Request
9
+ include Signature
10
+
11
+ # 拼接请求的接口的路径
12
+ #
13
+ # @param path [String] 接口的相对路径,e.g. "/user/getToken" 或者 "user/getToken",代码中自动处理开头的斜杠
14
+ # @return [URI] 请求全路径生成的 URI 对象,自动追加 .json 格式扩展名
15
+ #
16
+ def get_uri(path)
17
+ url = "#{RongCloud::Configuration.host}/#{path.gsub(/^\//, "")}"
18
+ URI(url.end_with?(".json") ? url : "#{url}.json")
19
+ end
20
+
21
+ # 实例化请求对象
22
+ #
23
+ # @param uri [URI] 请求路径对象
24
+ # @param params [Hash] 请求的参数,所有参数通过 form encoded data 方式发送
25
+ # @return [Net::HTTP::Post] 请求的实例
26
+ #
27
+ def initialize_request(uri, params)
28
+ req = Net::HTTP::Post.new(uri)
29
+ req.set_form_data(params) if params.respond_to?(:map)
30
+ signed_headers.each { |header, value| req[header] = value }
31
+
32
+ req
33
+ end
34
+
35
+ # 解析响应数据,包含错误检测
36
+ #
37
+ # @param res [HTTPResponse] 响应结果的实例
38
+ # @return [Hash] JSON 解析后的响应正文
39
+ # @raise [RongCloud::BadRequest] 请求参数有误,缺失或者不正确等,详见官方文档
40
+ #
41
+ def handle_response(res)
42
+ json = JSON.parse(res.body)
43
+ case res
44
+ when Net::HTTPOK
45
+ json
46
+ else
47
+ error = (HTTP_CODE_TO_ERRORS_MAP[res.code] || RequestError).new(json["errorMessage"])
48
+ error.business_code = json["code"]
49
+ raise error
50
+ end
51
+ end
52
+
53
+ # 执行请求
54
+ # @param path [String] 请求 API 的相对路径
55
+ # @param params [Hash] 请求的参数
56
+ # @return [Hash] JSON 解析后的响应数据
57
+ # @raise [RongCloud::BadRequest] 请求参数有误,缺失或者不正确等,详见官方文档
58
+ def request(path, params = nil)
59
+ uri = get_uri(path)
60
+ req = initialize_request(uri, params)
61
+ use_ssl = uri.scheme == 'https'
62
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
63
+ http.request(req)
64
+ end
65
+
66
+ handle_response(res)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,17 @@
1
+ require 'rong_cloud/request'
2
+ require 'rong_cloud/services/user'
3
+ require 'rong_cloud/services/message'
4
+ require 'rong_cloud/services/wordfilter'
5
+ require 'rong_cloud/services/group'
6
+
7
+ module RongCloud
8
+ # 封装所有接口的 Service 类,所有业务接口通过 Service 的实例完成调用
9
+ #
10
+ class Service
11
+ include RongCloud::Request
12
+ include RongCloud::Services::User
13
+ include RongCloud::Services::Message
14
+ include RongCloud::Services::Wordfilter
15
+ include RongCloud::Services::Group
16
+ end
17
+ end
@@ -0,0 +1,73 @@
1
+ module RongCloud
2
+ module Services
3
+ # 群组服务相关接口 http://www.rongcloud.cn/docs/server.html#群组服务
4
+ module Group
5
+ # 同步用户所属群组
6
+ #
7
+ # @param user_id [String] 用户在融云的 id
8
+ # @param groups [Hash] 用户需要同步的群组信息,以群组 id 为键,以群组名称为值
9
+ #
10
+ def sync_group(user_id, groups)
11
+ params = {userId: user_id}
12
+ groups.each do |id, name|
13
+ params["group[#{id}]"] = name
14
+ end
15
+
16
+ request("/group/sync", params)
17
+ end
18
+
19
+ # 创建群组
20
+ #
21
+ def create_group(user_id, group_id, group_name)
22
+ request("/group/create", userId: user_id, groupId: group_id, groupName: group_name)
23
+ end
24
+
25
+ # 加入群组
26
+ #
27
+ def join_group(user_id, group_id, group_name)
28
+ request("/group/join", userId: user_id, groupId: group_id, groupName: group_name)
29
+ end
30
+
31
+ # 退出群组
32
+ #
33
+ def quit_group(user_id, group_id)
34
+ request("/group/quit", userId: user_id, groupId: group_id)
35
+ end
36
+
37
+ # 解散群组
38
+ #
39
+ def dismiss_group(user_id, group_id)
40
+ request("/group/dismiss", userId: user_id, groupId: group_id)
41
+ end
42
+
43
+ # 刷新群组信息
44
+ def refresh_group(group_id, group_name)
45
+ request("/group/refresh", groupId: group_id, groupName: group_name)
46
+ end
47
+
48
+ # 查询群成员
49
+ #
50
+ def group_members(group_id)
51
+ request("/group/user/query", groupId: group_id)
52
+ end
53
+
54
+ # 添加禁言群成员
55
+ #
56
+ def block_group_member(user_id, group_id, minute)
57
+ request("/group/user/gag/add", userId: user_id, groupId: group_id, minute: minute)
58
+ end
59
+
60
+ # 移除禁言群成员
61
+ #
62
+ def unblock_group_member(user_id, group_id)
63
+ request("/group/user/gag/rollback", userId: user_id, groupId: group_id)
64
+ end
65
+
66
+ # 查询被禁言群成员
67
+ #
68
+ def blocked_group_members(group_id)
69
+ request("/group/user/gag/list", groupId: group_id)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,45 @@
1
+ module RongCloud
2
+ module Services
3
+ module Message
4
+ # 消息发送渠道,区分私信、系统消息、群组消息、讨论组消息、聊天室消息以及广播消息
5
+ class MessageChannel
6
+ # 各消息渠道各自对应请求路径以及特殊参数名
7
+ #
8
+ CHANNEL_TO_REQUEST_DETAILS_MAP = {
9
+ 'private': { target_param_name: "toUserId", api_path: "/message/private/publish" },
10
+ system: { target_param_name: "toUserId", api_path: "/message/system/publish" },
11
+ group: { target_param_name: 'toGroupId', api_path: "/message/group/publish" },
12
+ discussion: { target_param_name: "toDiscussionId", api_path: "/message/discussion/publish" },
13
+ chatroom: { target_param_name: "toChatroomId", api_path: "/message/chatroom/publish" },
14
+ broadcast: { api_path: "/message/broadcast" }
15
+ }.freeze
16
+ # 支持的消息渠道的列表
17
+ #
18
+ VALID_CHANNEL_NAMES = CHANNEL_TO_REQUEST_DETAILS_MAP.keys.map(&:to_s).freeze
19
+
20
+ # 实例化消息渠道对象
21
+ #
22
+ # @param channel_name [String] 渠道名称
23
+ # @return [RongCloud::Services::Message::MessageChannel] 消息渠道实例
24
+ # @raise [RongCloud::UnsupportedMessageChannelName] 消息渠道不支持
25
+ #
26
+ def initialize(channel_name)
27
+ if VALID_CHANNEL_NAMES.include?(channel_name.to_s)
28
+ @channel_name = channel_name.to_s.to_sym
29
+ else
30
+ raise UnsupportedMessageChannelName,
31
+ "support only channels: #{VALID_CHANNEL_NAMES}"
32
+ end
33
+ end
34
+
35
+ def target_param_name
36
+ CHANNEL_TO_REQUEST_DETAILS_MAP[@channel_name][:target_param_name]
37
+ end
38
+
39
+ def api_path
40
+ CHANNEL_TO_REQUEST_DETAILS_MAP[@channel_name][:api_path]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,58 @@
1
+ require 'rong_cloud/services/message/message_channel'
2
+
3
+ module RongCloud
4
+ module Services
5
+ # 消息发送相关接口 http://www.rongcloud.cn/docs/server.html#消息发送服务
6
+ module Message
7
+ # 通用的发送消息方法
8
+ # @param from_user_id [String] 消息发起人 id
9
+ # @param target_id [String, Integer] 消息接收方,用户 id、群组 id、讨论组 id、聊天室 id 等
10
+ # @param channel_name [String] 消息通道,表示单聊、系统、群组、讨论组、聊天室或者广播消息
11
+ # @param object_name [String] 消息类型名称,内置消息类型或者自定义消息类型名称
12
+ # @param [Hash] content 消息体,更多消息请看 http://www.rongcloud.cn/docs/server.html#内置消息类型表
13
+ # @option content [Object] :content 消息体中的正文,如果为自定义消息,由消息消费方自行解析
14
+ # @option content [Object] :extra 消息体中的附加信息,传递时为字符串,由消息消费方自行解析
15
+ #
16
+ # @param [Hash] options 额外选项,包含 pushContent 以及 pushData 等配置,所有支持选项根据各消息类型确定
17
+ # @option options [String] :pushContent 推送通知显示的 Push 内容
18
+ # @option options [Hash] :pushData 推送 payload
19
+ #
20
+ def send_message(from_user_id, target_id, channel_name, object_name, content, options = {})
21
+ message_channel = MessageChannel.new(channel_name)
22
+ params = { fromUserId: from_user_id, objectName: object_name, content: content.to_json }
23
+ if message_channel.target_param_name
24
+ params.merge!(message_channel.target_param_name => target_id)
25
+ end
26
+
27
+ params.merge!(options)
28
+ request(message_channel.api_path, params)
29
+ end
30
+
31
+ # 发送单聊消息 http://www.rongcloud.cn/docs/server.html#发送单聊消息_方法
32
+ #
33
+ def send_private_message(from_user_id, to_user_id, object_name, content, options = {})
34
+ send_message(from_user_id, to_user_id, :private, object_name, content, options)
35
+ end
36
+
37
+ # 发送系统消息 http://www.rongcloud.cn/docs/server.html#发送系统消息_方法
38
+ def send_system_message(from_user_id, to_user_id, object_name, content, options = {})
39
+ send_message(from_user_id, to_user_id, :system, object_name, content, options)
40
+ end
41
+
42
+ # 发送群组消息 http://www.rongcloud.cn/docs/server.html#发送群组消息_方法
43
+ def send_group_message(from_user_id, to_group_id, object_name, content, options = {})
44
+ send_message(from_user_id, to_group_id, :group, object_name, content, options)
45
+ end
46
+
47
+ # 发送讨论组消息 http://www.rongcloud.cn/docs/server.html#发送讨论组消息_方法
48
+ def send_discussion_message(from_user_id, to_discussion_id, object_name, content, options = {})
49
+ send_message(from_user_id, to_discussion_id, :discussion, object_name, content, options)
50
+ end
51
+
52
+ # 发送广播消息 http://www.rongcloud.cn/docs/server.html#发送广播消息_方法
53
+ def send_broadcast_message(from_user_id, object_name, content, options = {})
54
+ send_message(from_user_id, nil, :broadcast, object_name, content, options)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,70 @@
1
+ module RongCloud
2
+ module Services
3
+ # 用户相关接口 http://www.rongcloud.cn/docs/server.html#用户服务
4
+ module User
5
+ # 获取 Token,即创建融云用户
6
+ #
7
+ # @param user_id [String] 用户 Id,最大长度 64 字节。是用户在 App 中的唯一标识码,必须保证在同一个 App 内不重复,重复的用户 Id 将被当作是同一用户
8
+ # @param name [String] 用户名称,最大长度 128 字节。用来在 Push 推送时显示用户的名称。
9
+ # @param portrait_uri [String] 用户头像 URI,最大长度 1024 字节。用来在 Push 推送时显示用户的头像。
10
+ # @return [Hash] 请求响应结果数据
11
+ #
12
+ def get_token(user_id, name, portrait_uri)
13
+ request("/user/getToken", {userId: user_id, name: name, portraitUri: portrait_uri})
14
+ end
15
+
16
+ # 刷新用户信息
17
+ #
18
+ def refresh_user(user_id, name = nil, portrait_uri = nil)
19
+ params = { userId: user_id, name: name, portraitUri: portrait_uri }
20
+ params.reject!{|key, value| value.nil?}
21
+
22
+ request("/user/refresh", params)
23
+ end
24
+
25
+ # 检查用户在线状态
26
+ #
27
+ def check_online(user_id)
28
+ request("/user/checkOnline", { userId: user_id })
29
+ end
30
+
31
+ # 封禁用户
32
+ # @param user_id [String] 用户在融云的用户 id
33
+ # @param minute [Integer] 封禁时长
34
+ #
35
+ def block_user(user_id, minute)
36
+ request("/user/block", { userId: user_id, minute: minute })
37
+ end
38
+
39
+ # 解除用户封禁
40
+ #
41
+ def unblock_user(user_id)
42
+ request("/user/unblock", userId: user_id)
43
+ end
44
+
45
+ # 查询被封禁用户列表
46
+ #
47
+ def blocked_users
48
+ request("/user/block/query")
49
+ end
50
+
51
+ # 添加用户到黑名单
52
+ #
53
+ def blacklist_add(user_id, black_user_id)
54
+ request("/user/blacklist/add", userId: user_id, blackUserId: black_user_id)
55
+ end
56
+
57
+ # 从黑名单中移除用户
58
+ #
59
+ def blacklist_remove(user_id, black_user_id)
60
+ request("/user/blacklist/remove", userId: user_id, blackUserId: black_user_id)
61
+ end
62
+
63
+ # 获取某用户的黑名单列表
64
+ #
65
+ def blacklisted_users(user_id)
66
+ request("/user/blacklist/query", userId: user_id)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,26 @@
1
+ module RongCloud
2
+ module Services
3
+ # 敏感词相关接口 http://www.rongcloud.cn/docs/server.html#敏感词服务
4
+ module Wordfilter
5
+ # 添加敏感词
6
+ #
7
+ # @param word [String] 敏感词
8
+ #
9
+ def add_wordfilter(word)
10
+ request("/wordfilter/add", { word: word })
11
+ end
12
+
13
+ # 移除敏感词
14
+ #
15
+ def delete_wordfilter(word)
16
+ request("/wordfilter/delete", { word: word })
17
+ end
18
+
19
+ # 查询已有敏感词的列表
20
+ #
21
+ def wordfilter_list
22
+ request("/wordfilter/list")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ require 'digest/sha1'
2
+
3
+ module RongCloud
4
+ # 签名算法相关模块
5
+ # http://www.rongcloud.cn/docs/server.html#通用_API_接口签名规则
6
+ #
7
+ module Signature
8
+ # 生成签名串
9
+ #
10
+ # @param nonce [String] 参与签名的随机字串,无长度限制
11
+ # @param timestamp [String] 时间戳,从 1970 年 1 月 1 日 0 点 0 分 0 秒开始到现在的秒数
12
+ # @return [String] 签名完成之后的结果字串
13
+ #
14
+ def signature(nonce, timestamp)
15
+ str = "#{RongCloud::Configuration.app_secret}#{nonce}#{timestamp}"
16
+ Digest::SHA1.hexdigest(str)
17
+ end
18
+
19
+ # API 调用签名所需的请求头,包含签名等
20
+ # @note 包含以下请求头:
21
+ # App-Key
22
+ # Nonce
23
+ # Timestamp
24
+ # Signature
25
+ # @return [Hash] 签名所需请求头
26
+ def signed_headers
27
+ nonce = rand(10**6)
28
+ timestamp = Time.now.to_i
29
+ signature = signature(nonce, timestamp)
30
+
31
+ {
32
+ 'App-Key' => RongCloud::Configuration.app_key,
33
+ 'Nonce' => nonce,
34
+ 'Timestamp' => timestamp,
35
+ 'Signature' => signature
36
+ }
37
+ end
38
+ end
39
+ end
data/lib/rong_cloud.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rong_cloud/configuration'
2
+ require 'rong_cloud/errors'
3
+ require 'rong_cloud/service'
4
+
5
+ # 融云 Ruby SDK 的全局命名空间
6
+ module RongCloud
7
+ # 配置融云 Server 连接信息
8
+ #
9
+ def self.configure(&block)
10
+ yield RongCloud::Configuration
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'rong_cloud_server'
3
+ s.platform = Gem::Platform::RUBY
4
+ s.require_path = 'lib'
5
+ s.summary = '融云 Server API SDK'
6
+ s.description = '融云服务器端接口 API,http://www.rongcloud.cn/docs/server.html'
7
+ s.version = '0.0.1'
8
+ s.files = `git ls-files`.split("\n")
9
+ s.authors = ['Martin Hong']
10
+ s.email = 'hongzeqin@gmail.com'
11
+ s.homepage = 'http://blog.hackerpie.com/rong_cloud/'
12
+ s.license = 'MIT'
13
+ end
data/run_tests.sh ADDED
@@ -0,0 +1 @@
1
+ echo "Dir.glob('./test/**/*_test.rb').each { |file| require file}" | ruby -Itest -Ilib
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ module RongCloud
4
+ class ConfigurationTest < Minitest::Test
5
+ def setup
6
+ RongCloud.configure do |config|
7
+ config.host = nil
8
+ end
9
+ end
10
+
11
+ def test_app_key_settings_and_reading
12
+ RongCloud::Configuration.app_key = "test_key"
13
+ assert_equal "test_key", RongCloud::Configuration.app_key
14
+ end
15
+
16
+ def test_app_secret_settings_and_reading
17
+ RongCloud::Configuration.app_secret = "test_secret"
18
+ assert_equal "test_secret", RongCloud::Configuration.app_secret
19
+ end
20
+
21
+ def test_logger_setting_and_reading
22
+ logger = ::Logger.new("rong_cloud.log")
23
+ RongCloud::Configuration.logger = logger
24
+ assert_equal logger, RongCloud::Configuration.logger
25
+ end
26
+
27
+ def test_default_host_and_setting_and_reading
28
+ assert_equal "https://api.cn.ronghub.com", RongCloud::Configuration.host
29
+ RongCloud::Configuration.host = "http://other.host.com"
30
+ assert_equal "http://other.host.com", RongCloud::Configuration.host
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ module RongCloud
4
+ class RequestTest < Minitest::Test
5
+ include RongCloud::Request
6
+
7
+ def setup
8
+ rong_cloud_configure_with_settings
9
+ end
10
+
11
+ def test_get_uri_path_end_with_json_format
12
+ uri = get_uri("users.json")
13
+ assert_equal "/users.json", uri.path
14
+ assert_equal "api.cn.ronghub.com", uri.host
15
+ end
16
+
17
+ def test_get_uri_path_not_end_with_json_format
18
+ uri = get_uri("users")
19
+ assert_equal "/users.json", uri.path
20
+ assert_equal "api.cn.ronghub.com", uri.host
21
+ end
22
+
23
+ def test_request_with_invalid_app_key
24
+ RongCloud::Configuration.app_key = "xxx"
25
+ error = assert_raises RongCloud::BadRequest do
26
+ request("/user/getToken", {userId: 'user', name: "User"})
27
+ end
28
+ assert_equal "invalid App-Key.", error.message
29
+ assert_equal 1002, error.business_code
30
+ end
31
+
32
+ def test_request_with_invalid_app_secret
33
+ RongCloud::Configuration.app_secret = "xxx"
34
+ error = assert_raises RongCloud::AuthenticationFailed do
35
+ request("/user/getToken", {userId: 'user', name: "User"})
36
+ end
37
+ assert_equal "签名错误,请检查。", error.message
38
+ assert_equal 1004, error.business_code
39
+ end
40
+
41
+ def test_request_with_missing_required_field
42
+ error = assert_raises RongCloud::BadRequest do
43
+ request("/user/getToken")
44
+ end
45
+ assert_equal "userId is required.", error.message
46
+ assert_equal 1002, error.business_code
47
+ end
48
+
49
+ def test_request_with_valid_params
50
+ response = request("/user/getToken", { userId: 'user', name: "User", portraitUri: "uri" })
51
+ assert_equal 200, response["code"]
52
+ assert_equal "user", response["userId"]
53
+ assert response["token"]
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+ require 'rong_cloud/services/user_test'
3
+ require 'rong_cloud/services/message_test'
4
+ require 'rong_cloud/services/wordfilter_test'
5
+ require 'rong_cloud/services/group_test'
6
+
7
+ module RongCloud
8
+ class ServiceTest < Minitest::Test
9
+ include RongCloud::Services::UserTest
10
+ include RongCloud::Services::MessageTest
11
+ include RongCloud::Services::WordfilterTest
12
+ include RongCloud::Services::GroupTest
13
+
14
+ def setup
15
+ rong_cloud_configure_with_settings
16
+ @service = RongCloud::Service.new
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,102 @@
1
+ module RongCloud
2
+ module Services
3
+ module GroupTest
4
+ def test_sync_group
5
+ response = @service.sync_group('user1', '1' => 'group1', '2' => 'group2')
6
+ assert_equal 200, response['code']
7
+ end
8
+
9
+ def test_create_group_with_single_user_id
10
+ response = @service.create_group('user1', '3', 'group3')
11
+ assert_equal 200, response['code']
12
+ end
13
+
14
+ def test_create_group_with_multiple_user_id
15
+ response = @service.create_group(%w(user1 user2 user3), '4', 'group4')
16
+ assert_equal 200, response['code']
17
+ end
18
+
19
+ def test_create_group_without_group_id
20
+ error = assert_raises RongCloud::BadRequest do
21
+ @service.create_group('user1', nil, 'group3')
22
+ end
23
+ assert_equal 'groupId is required.', error.message
24
+ end
25
+
26
+ def test_create_group_without_group_name
27
+ response = @service.create_group('user1', '5', nil)
28
+ assert_equal 200, response['code']
29
+ end
30
+
31
+ def test_join_group_with_single_user_id
32
+ response = @service.join_group('user1', '3', 'group3')
33
+ assert_equal 200, response['code']
34
+ end
35
+
36
+ def test_join_group_with_multiple_user_id
37
+ response = @service.join_group(%w(user1 user2 user3), '4', 'group4')
38
+ assert_equal 200, response['code']
39
+ end
40
+
41
+ def test_join_group_without_group_id
42
+ error = assert_raises RongCloud::BadRequest do
43
+ @service.join_group('user1', nil, 'group3')
44
+ end
45
+ assert_equal 'groupId is required.', error.message
46
+ end
47
+
48
+ def test_join_group_without_group_name
49
+ response = @service.join_group('user1', '5', nil)
50
+ assert_equal 200, response['code']
51
+ end
52
+
53
+ def test_quit_group
54
+ response = @service.quit_group('user', 'group1')
55
+ assert_equal 200, response['code']
56
+ end
57
+
58
+ def test_dismiss_group
59
+ response = @service.dismiss_group('user', 'group1')
60
+ assert_equal 200, response['code']
61
+ end
62
+
63
+ def test_refresh_group
64
+ response = @service.refresh_group('group1', "测试群组1")
65
+ assert_equal 200, response['code']
66
+ end
67
+
68
+ def test_group_members
69
+ @service.create_group("user1", "group6", "测试群组成员")
70
+ @service.join_group("user2", "group6", "测试群组成员")
71
+ response = @service.group_members("group6")
72
+ user_ids = response['users'].map{|user| user['id'] }
73
+
74
+ assert_equal 2, user_ids.count
75
+ assert_includes user_ids, 'user1'
76
+ assert_includes user_ids, 'user2'
77
+ end
78
+
79
+ def test_block_group_member
80
+ response = @service.block_group_member("user1", "group1", 600)
81
+ assert_equal 200, response['code']
82
+ end
83
+
84
+ def test_unblock_group_member
85
+ response = @service.unblock_group_member("user1", "group1")
86
+ assert_equal 200, response['code']
87
+ end
88
+
89
+ def test_blocked_group_members
90
+ @service.create_group("user1", "group1", "测试群组禁言服务")
91
+ @service.join_group("user2", "group1", "测试群组禁言服务")
92
+ @service.block_group_member("user2", "group1", 60000)
93
+
94
+ response = @service.blocked_group_members('group1')
95
+ user_ids = response['users'].map{|user| user['userId']}
96
+
97
+ assert_equal 1, user_ids.count
98
+ assert_includes user_ids, "user2"
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,82 @@
1
+ require 'rong_cloud/services/message/message_channel'
2
+
3
+ module RongCloud
4
+ module Services
5
+ module Message
6
+ class MessageChannelTest < Minitest::Test
7
+ def test_initialize_with_supported_channel_name
8
+ channel = RongCloud::Services::Message::MessageChannel.new(:private)
9
+ assert channel
10
+ end
11
+
12
+ def test_initialize_with_unsupported_channel_name
13
+ error = assert_raises RongCloud::UnsupportedMessageChannelName do
14
+ RongCloud::Services::Message::MessageChannel.new(:nothing)
15
+ end
16
+ expected_error = "support only channels: [\"private\", \"system\", \"group\", \"discussion\", \"chatroom\", \"broadcast\"]"
17
+ assert_equal expected_error, error.message
18
+ end
19
+
20
+ def test_target_param_name_for_private
21
+ channel = RongCloud::Services::Message::MessageChannel.new(:private)
22
+ assert_equal "toUserId", channel.target_param_name
23
+ end
24
+
25
+ def test_target_param_name_for_system
26
+ channel = RongCloud::Services::Message::MessageChannel.new(:system)
27
+ assert_equal "toUserId", channel.target_param_name
28
+ end
29
+
30
+ def test_target_param_name_for_group
31
+ channel = RongCloud::Services::Message::MessageChannel.new(:group)
32
+ assert_equal "toGroupId", channel.target_param_name
33
+ end
34
+
35
+ def test_target_param_name_for_discussion
36
+ channel = RongCloud::Services::Message::MessageChannel.new(:discussion)
37
+ assert_equal "toDiscussionId", channel.target_param_name
38
+ end
39
+
40
+ def test_target_param_name_for_chatroom
41
+ channel = RongCloud::Services::Message::MessageChannel.new(:chatroom)
42
+ assert_equal "toChatroomId", channel.target_param_name
43
+ end
44
+
45
+ def test_target_param_name_for_broadcast
46
+ channel = RongCloud::Services::Message::MessageChannel.new(:broadcast)
47
+ assert_nil channel.target_param_name
48
+ end
49
+
50
+ def test_api_path_for_private
51
+ channel = RongCloud::Services::Message::MessageChannel.new(:private)
52
+ assert_equal "/message/private/publish", channel.api_path
53
+ end
54
+
55
+ def test_api_path_for_system
56
+ channel = RongCloud::Services::Message::MessageChannel.new(:system)
57
+ assert_equal "/message/system/publish", channel.api_path
58
+ end
59
+
60
+ def test_api_path_for_group
61
+ channel = RongCloud::Services::Message::MessageChannel.new(:group)
62
+ assert_equal "/message/group/publish", channel.api_path
63
+ end
64
+
65
+ def test_api_path_for_discussion
66
+ channel = RongCloud::Services::Message::MessageChannel.new(:discussion)
67
+ assert_equal "/message/discussion/publish", channel.api_path
68
+ end
69
+
70
+ def test_api_path_for_chatroom
71
+ channel = RongCloud::Services::Message::MessageChannel.new(:chatroom)
72
+ assert_equal "/message/chatroom/publish", channel.api_path
73
+ end
74
+
75
+ def test_api_path_for_broadcast
76
+ channel = RongCloud::Services::Message::MessageChannel.new(:broadcast)
77
+ assert_equal "/message/broadcast", channel.api_path
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,83 @@
1
+ module RongCloud
2
+ module Services
3
+ module MessageTest
4
+ def test_send_private_message_with_single_to_user_id
5
+ response = @service.send_private_message(1, 2, "RC:TxtMsg", { content: "hello world", extra: "nothing" })
6
+ assert_equal 200, response["code"]
7
+ end
8
+
9
+ def test_send_private_message_with_multiple_to_user_ids
10
+ response = @service.send_private_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" })
11
+ assert_equal 200, response["code"]
12
+ end
13
+
14
+ def test_send_private_message_with_options
15
+ options = { pushContent: "hello", pushData: { shouldBeTrue: "true" } }
16
+ response = @service.send_private_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" }, options)
17
+ assert_equal 200, response["code"]
18
+ end
19
+
20
+ def test_send_system_message_with_single_to_user_id
21
+ response = @service.send_system_message(1, 2, "RC:TxtMsg", { content: "hello world", extra: "nothing" })
22
+ assert_equal 200, response["code"]
23
+ end
24
+
25
+ def test_send_system_message_with_multiple_to_user_ids
26
+ response = @service.send_system_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" })
27
+ assert_equal 200, response["code"]
28
+ end
29
+
30
+ def test_send_system_message_with_options
31
+ options = { pushContent: "hello", pushData: { shouldBeTrue: "true" } }
32
+ response = @service.send_system_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" }, options)
33
+ assert_equal 200, response["code"]
34
+ end
35
+
36
+ def test_send_group_message_with_single_to_group_id
37
+ response = @service.send_group_message(1, 2, "RC:TxtMsg", { content: "hello world", extra: "nothing" })
38
+ assert_equal 200, response["code"]
39
+ end
40
+
41
+ def test_send_group_message_with_multiple_to_group_ids
42
+ response = @service.send_group_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" })
43
+ assert_equal 200, response["code"]
44
+ end
45
+
46
+ def test_send_group_message_with_multiple_to_too_many_group_ids
47
+ error = assert_raises RongCloud::BadRequest do
48
+ @service.send_group_message(1, [2, 3, 4, 5], "RC:TxtMsg", { content: "hello world", extra: "nothing" })
49
+ end
50
+
51
+ assert_equal "the number of toUserId should less than 3.", error.message
52
+ assert_equal 1002, error.business_code
53
+ end
54
+
55
+ def test_send_group_message_with_options
56
+ options = { pushContent: "hello", pushData: { shouldBeTrue: "true" } }
57
+ response = @service.send_group_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" }, options)
58
+ assert_equal 200, response["code"]
59
+ end
60
+
61
+ def test_send_discussion_message_with_single_to_discussion_id
62
+ response = @service.send_discussion_message(1, 2, "RC:TxtMsg", { content: "hello world", extra: "nothing" })
63
+ assert_equal 200, response["code"]
64
+ end
65
+
66
+ def test_send_discussion_message_with_multiple_to_discussion_ids
67
+ response = @service.send_discussion_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" })
68
+ assert_equal 200, response["code"]
69
+ end
70
+
71
+ def test_send_discussion_message_with_options
72
+ options = { pushContent: "hello", pushData: { shouldBeTrue: "true" } }
73
+ response = @service.send_discussion_message(1, [2, 3, 4], "RC:TxtMsg", { content: "hello world", extra: "nothing" }, options)
74
+ assert_equal 200, response["code"]
75
+ end
76
+
77
+ def test_send_broadcast_message
78
+ response = @service.send_broadcast_message(1, "RC:TxtMsg", { content: "hello world", extra: "nothing" })
79
+ assert_equal 200, response["code"]
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,144 @@
1
+ module RongCloud
2
+ module Services
3
+ module UserTest
4
+ def test_get_token_without_user_id
5
+ error = assert_raises RongCloud::BadRequest do
6
+ @service.get_token(nil, nil, nil)
7
+ end
8
+ assert_equal 1002, error.business_code
9
+ assert_equal "userId is required.", error.message
10
+ end
11
+
12
+ def test_get_token_without_name
13
+ response = @service.get_token("user", nil, nil)
14
+ assert response["token"]
15
+ end
16
+
17
+ def test_get_token_without_portrait_url
18
+ response = @service.get_token("user", "User", nil)
19
+ assert response["token"]
20
+ end
21
+
22
+ def test_get_token_with_all_params
23
+ response = @service.get_token("user", "User", "fake_url")
24
+ assert response["token"]
25
+ end
26
+
27
+ def test_get_token_with_chinese_name
28
+ response = @service.get_token("user", "王二狗", "fake_url")
29
+ assert response["token"]
30
+ end
31
+
32
+ def test_refresh_user_without_user_id
33
+ error = assert_raises RongCloud::BadRequest do
34
+ @service.refresh_user(nil, nil, nil)
35
+ end
36
+ assert_equal 1002, error.business_code
37
+ assert_equal "userId is required.", error.message
38
+ end
39
+
40
+ def test_refresh_user_with_unexisted_user_id
41
+ error = assert_raises RongCloud::BadRequest do
42
+ @service.refresh_user("unexistedUserId", nil, nil)
43
+ end
44
+ assert_equal "userIdunexistedUserId is not exist.", error.message
45
+ assert_equal 1002, error.business_code
46
+ end
47
+
48
+ def test_refresh_user_without_portrait_url
49
+ response = @service.refresh_user("user", "User", nil)
50
+ assert_equal 200, response["code"]
51
+ end
52
+
53
+ def test_refresh_user_with_all_params
54
+ response = @service.refresh_user("user", "User", "fake_url")
55
+ assert_equal 200, response["code"]
56
+ end
57
+
58
+ def test_refresh_user_with_chinese_name
59
+ response = @service.refresh_user("user", "小李子", nil)
60
+ assert_equal 200, response["code"]
61
+ end
62
+
63
+ def test_check_online_without_user_id
64
+ error = assert_raises RongCloud::BadRequest do
65
+ @service.check_online(nil)
66
+ end
67
+ assert_equal "userId is required.", error.message
68
+ assert_equal 1002, error.business_code
69
+ end
70
+
71
+ def test_check_online_with_unexisted_user_id
72
+ response = @service.check_online("unexistedUserId")
73
+ assert_equal "0", response["status"]
74
+ end
75
+
76
+ def test_check_online_with_existed_user_id
77
+ response = @service.check_online("user")
78
+ assert_equal "0", response["status"]
79
+ end
80
+
81
+ def test_block_user_without_user_id
82
+ error = assert_raises RongCloud::BadRequest do
83
+ @service.block_user(nil, nil)
84
+ end
85
+ assert_equal "userId is required.", error.message
86
+ assert_equal 1002, error.business_code
87
+ end
88
+
89
+ def test_block_user_without_minute
90
+ @service.get_token("blocked_user", "Blocked", "fake_url")
91
+ error = assert_raises RongCloud::BadRequest do
92
+ @service.block_user("blocked_user", nil)
93
+ end
94
+
95
+ assert_equal "minute is required.", error.message
96
+ assert_equal 1002, error.business_code
97
+ end
98
+
99
+ def test_block_user_without_unexisted_user_id
100
+ response = @service.block_user("unexistedUserId", 3)
101
+ assert_equal 200, response["code"]
102
+ end
103
+
104
+ def test_block_user_and_blocked_users_and_unblock_user
105
+ @service.get_token("blocked_user2", "Blocked", "fake_url")
106
+ response = @service.block_user("blocked_user2", 10)
107
+ assert_equal 200, response["code"]
108
+
109
+ users = @service.blocked_users["users"]
110
+ user = users.detect{|object| object["userId"] == "blocked_user2"}
111
+ assert user["blockEndTime"]
112
+
113
+ response = @service.unblock_user("blocked_user2")
114
+ assert 200, response["code"]
115
+ end
116
+
117
+ def test_blacklist_add_for_unexisted_user_id
118
+ response = @service.blacklist_add("unexisted", "blacklisted_user")
119
+ assert_equal 200, response["code"]
120
+ end
121
+
122
+ def test_blacklist_add_for_existed_user_with_multiple_black_user_id
123
+ response = @service.blacklist_add("user", %w(blu blu2 blu3))
124
+ assert_equal 200, response["code"]
125
+ end
126
+
127
+ def test_blacklist_remove_for_unexisted_user_id
128
+ response = @service.blacklist_remove("unexisted_user", "blacklisted_user")
129
+ assert_equal 200, response["code"]
130
+ end
131
+
132
+ def test_blacklist_remove_for_unblacklisted_user_id
133
+ response = @service.blacklist_remove("user", "unexisted_user")
134
+ assert_equal 200, response["code"]
135
+ end
136
+
137
+ def test_blacklisted_users
138
+ @service.blacklist_add("user", %w(user2 user3))
139
+ response = @service.blacklisted_users("user")
140
+ assert %w(user2 user3) & response["users"] == %w(user2 user3)
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,30 @@
1
+ module RongCloud
2
+ module Services
3
+ module WordfilterTest
4
+ def test_add_wordfilter_with_single_word
5
+ response = @service.add_wordfilter("敏感词")
6
+ assert_equal 200, response["code"]
7
+ end
8
+
9
+ def test_delete_exist_wordfilter
10
+ @service.add_wordfilter("hello")
11
+ response = @service.delete_wordfilter("hello")
12
+ assert_equal 200, response["code"]
13
+ end
14
+
15
+ def test_delete_unexist_wordfilter
16
+ response = @service.delete_wordfilter("unexistedWord")
17
+ assert_equal 500, response["code"] # TODO: 500 非期待,后边修复
18
+ end
19
+
20
+ def test_wordfilter_list
21
+ @service.add_wordfilter("乱")
22
+ response = @service.wordfilter_list
23
+ words = response["words"].map{|word| word["word"]}
24
+ assert_includes words, "乱"
25
+
26
+ @service.delete_wordfilter("乱")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+
3
+ module RongCloud
4
+ class SignatureTest < Minitest::Test
5
+ include RongCloud::Signature
6
+
7
+ def setup
8
+ RongCloud.configure do |config|
9
+ config.app_key = "uwd1c0sxdlx2"
10
+ config.app_secret = "Y1W2MeFwwwRxa0"
11
+
12
+ @nonce = 14314
13
+ @timestamp = 1408706337
14
+ @expected_sign = "e107e3819638b81a00383951d1d871197910ffe6"
15
+ end
16
+ end
17
+
18
+ def test_signature
19
+ signature = signature(@nonce, @timestamp)
20
+ assert_equal @expected_sign, signature
21
+ end
22
+
23
+ def test_signed_headers
24
+ Time.stub :now, Time.at(@timestamp) do
25
+ def rand(args)
26
+ @nonce
27
+ end
28
+
29
+ expected_headers = {
30
+ 'App-Key' => RongCloud::Configuration.app_key,
31
+ 'Nonce' => @nonce,
32
+ 'Timestamp' => @timestamp,
33
+ 'Signature' => @expected_sign
34
+ }
35
+ assert_equal expected_headers, signed_headers
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ require 'test_helper'
2
+
3
+ class RondCloudTest < Minitest::Test
4
+ def test_configure
5
+ RongCloud.configure do |app|
6
+ app.app_key = "KEY"
7
+ app.app_secret = "SECRET"
8
+ app.host = "HOST"
9
+ end
10
+
11
+ assert_equal "KEY", RongCloud::Configuration.app_key
12
+ assert_equal "SECRET", RongCloud::Configuration.app_secret
13
+ assert_equal "HOST", RongCloud::Configuration.host
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'minitest/autorun'
2
+ require 'byebug'
3
+ require 'rong_cloud'
4
+ require 'yaml'
5
+
6
+ $settings = YAML.load_file("./config.yml")
7
+
8
+ def rong_cloud_configure_with_settings
9
+ RongCloud.configure do |config|
10
+ $settings.each do |setting, value|
11
+ config.send("#{setting}=", value)
12
+ end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rong_cloud_server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Martin Hong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: 融云服务器端接口 API,http://www.rongcloud.cn/docs/server.html
14
+ email: hongzeqin@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ".gitignore"
20
+ - README.md
21
+ - config.example.yml
22
+ - lib/rong_cloud.rb
23
+ - lib/rong_cloud/configuration.rb
24
+ - lib/rong_cloud/errors.rb
25
+ - lib/rong_cloud/request.rb
26
+ - lib/rong_cloud/service.rb
27
+ - lib/rong_cloud/services/group.rb
28
+ - lib/rong_cloud/services/message.rb
29
+ - lib/rong_cloud/services/message/message_channel.rb
30
+ - lib/rong_cloud/services/user.rb
31
+ - lib/rong_cloud/services/wordfilter.rb
32
+ - lib/rong_cloud/signature.rb
33
+ - rong_cloud.gemspec
34
+ - run_tests.sh
35
+ - test/rong_cloud/configuration_test.rb
36
+ - test/rong_cloud/request_test.rb
37
+ - test/rong_cloud/service_test.rb
38
+ - test/rong_cloud/services/group_test.rb
39
+ - test/rong_cloud/services/message/message_channel_test.rb
40
+ - test/rong_cloud/services/message_test.rb
41
+ - test/rong_cloud/services/user_test.rb
42
+ - test/rong_cloud/services/wordfilter_test.rb
43
+ - test/rong_cloud/signature_test.rb
44
+ - test/rong_cloud_test.rb
45
+ - test/test_helper.rb
46
+ homepage: http://blog.hackerpie.com/rong_cloud/
47
+ licenses:
48
+ - MIT
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 2.6.6
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: 融云 Server API SDK
70
+ test_files: []