baidu-sdk 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/Gemfile +17 -0
- data/Guardfile +6 -0
- data/HISTORY.md +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +161 -0
- data/Rakefile +4 -0
- data/baidu-pcs.gemspec +24 -0
- data/lib/baidu/configure.rb +27 -0
- data/lib/baidu/core.rb +19 -0
- data/lib/baidu/errors/error.rb +22 -0
- data/lib/baidu/oauth.rb +11 -0
- data/lib/baidu/oauth/client.rb +66 -0
- data/lib/baidu/oauth/flow/base.rb +44 -0
- data/lib/baidu/oauth/flow/code.rb +69 -0
- data/lib/baidu/oauth/flow/device.rb +75 -0
- data/lib/baidu/pcs.rb +19 -0
- data/lib/baidu/pcs/client.rb +1090 -0
- data/lib/baidu/session.rb +36 -0
- data/lib/baidu/support/cacert.pem +37 -0
- data/lib/baidu/support/request.rb +127 -0
- data/lib/baidu/support/util.rb +67 -0
- data/lib/baidu/version.rb +5 -0
- data/spec/baidu/core_spec.rb +20 -0
- data/spec/baidu/oauth/client_spec.rb +199 -0
- data/spec/baidu/pcs/client_spec.rb +878 -0
- data/spec/baidu/session_spec.rb +27 -0
- data/spec/baidu/support/request_spec.rb +58 -0
- data/spec/baidu/support/util_spec.rb +48 -0
- data/spec/fixtures/add_task.json +1 -0
- data/spec/fixtures/cancel_task.json +3 -0
- data/spec/fixtures/copy.json +7 -0
- data/spec/fixtures/delete.json +3 -0
- data/spec/fixtures/diff.json +17 -0
- data/spec/fixtures/empty.json +1 -0
- data/spec/fixtures/get_token_code.json +8 -0
- data/spec/fixtures/get_token_device.json +8 -0
- data/spec/fixtures/list.json +11 -0
- data/spec/fixtures/list_task_0.json +1 -0
- data/spec/fixtures/list_task_1.json +1 -0
- data/spec/fixtures/listrecycle.json +23 -0
- data/spec/fixtures/logo.png +0 -0
- data/spec/fixtures/meta.json +11 -0
- data/spec/fixtures/mkdir.json +7 -0
- data/spec/fixtures/move.json +8 -0
- data/spec/fixtures/query_task_0.json +24 -0
- data/spec/fixtures/query_task_1.json +22 -0
- data/spec/fixtures/quota.json +5 -0
- data/spec/fixtures/rapidupload.json +10 -0
- data/spec/fixtures/refresh_token.json +8 -0
- data/spec/fixtures/restore.json +1 -0
- data/spec/fixtures/search.json +11 -0
- data/spec/fixtures/stream_list.json +16 -0
- data/spec/fixtures/streaming.m3u8 +5 -0
- data/spec/fixtures/upload.json +9 -0
- data/spec/fixtures/upload_block.json +4 -0
- data/spec/fixtures/user_and_device_code.json +8 -0
- data/spec/spec_helper.rb +66 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 989e658f22320158d197a3dcad2a2bb02fd2bae0
|
4
|
+
data.tar.gz: 386a4d15d1237e93b6081d90c678328b6728774f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d1a83fcbeb4bd8eba7ebf195f2f9a62742be6271187288fbd346ed7e6dae040c343658760cc2230da600dd019ec681c848b4482307ca684e7a80667ae6c04d1e
|
7
|
+
data.tar.gz: cd3a8c489b63b9796988fadf59187825fbdf0d6eff4c4ba20f62863de57c622e636f8efa684e513ff978af847c320902505374a2de892479887e34aacffb4f39
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private
|
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
group :development do
|
4
|
+
gem 'guard-rspec'
|
5
|
+
gem 'terminal-notifier-guard' if /darwin/ =~ RUBY_PLATFORM
|
6
|
+
end
|
7
|
+
|
8
|
+
group :test, :development do
|
9
|
+
gem 'rake'
|
10
|
+
gem 'rspec'
|
11
|
+
gem 'webmock'
|
12
|
+
end
|
13
|
+
|
14
|
+
gem 'rubysl', platforms: :rbx
|
15
|
+
|
16
|
+
# Specify your gem's dependencies in baidu-pcs.gemspec
|
17
|
+
gemspec
|
data/Guardfile
ADDED
data/HISTORY.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Lonre Wang
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# Baidu SDK for Ruby [](https://travis-ci.org/lonre/baidu-sdk-ruby)
|
2
|
+
|
3
|
+
Unofficial Baidu REST API SDK for Ruby.
|
4
|
+
非官方百度开放接口 Ruby 开发包。
|
5
|
+
|
6
|
+
## 安装
|
7
|
+
|
8
|
+
如果你通过 Bundler 来管理你的应用,添加如下一行到应用的 Gemfile 中:
|
9
|
+
|
10
|
+
gem 'baidu-sdk'
|
11
|
+
|
12
|
+
然后在终端执行:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
或者直接安装:
|
17
|
+
|
18
|
+
$ gem install baidu-sdk
|
19
|
+
|
20
|
+
`baidu-sdk` 当前支持 Ruby(MRI) 版本:1.9.3, 2.0.0
|
21
|
+
|
22
|
+
## 使用简介
|
23
|
+
[baidu-sdk api doc](http://rubydoc.info/gems/baidu-sdk/frames)
|
24
|
+
|
25
|
+
本项目根据百度开放接口划分成多个独立模块,如:`Baidu::OAuth`, `Baidu::PCS`等等。
|
26
|
+
|
27
|
+
`Hash` 必须使用 `symbol` key,无论是返回值或作为参数传递给方法。
|
28
|
+
|
29
|
+
涉及到调用 Baidu REST API 并返回 Hash 时,除非特别说明,Hash 是在原始 API 返回的 JSON 结果之上 `parse(JSON.parse(resp.body, symbolize_names: true))` 而来,Hash 和 JSON 拥有相同的结构,返回的具体结构可以参考各个方法的 API Doc。如:
|
30
|
+
```ruby
|
31
|
+
# 返回的原始 JSON
|
32
|
+
# {"quota":15000000000,"used":5221166,"request_id":4043312634}
|
33
|
+
result = pcs_client.quota
|
34
|
+
result[:quota] # => 15000000000
|
35
|
+
result[:used] # => 5221166
|
36
|
+
```
|
37
|
+
|
38
|
+
## OAuth
|
39
|
+
首先确保已经在百度开发者中心创建了应用。如果还没有创建应用,请通过此链接 [百度开发者中心](http://developer.baidu.com/console) 创建一个新的应用,获取应用的 `API Key` 和 `Secret Key`。
|
40
|
+
|
41
|
+
1. 使用全局配置方式:
|
42
|
+
```ruby
|
43
|
+
Baidu.config do |c|
|
44
|
+
c.client_id = 'an_api_key'
|
45
|
+
c.client_secret = 'a_securet_key'
|
46
|
+
end
|
47
|
+
```
|
48
|
+
此全局配置为可选方式,还可以在新建 `Baidu::OAuth::Client` 实例的时候指定 `API Key` 和 `Secret Key`。
|
49
|
+
|
50
|
+
2. 创建 Baidu::OAuth::Client 实例
|
51
|
+
```ruby
|
52
|
+
require 'baidu/oauth'
|
53
|
+
client = Baidu::OAuth::Client.new
|
54
|
+
# 或者
|
55
|
+
client = Baidu::OAuth::Client.new('a_client_id', 'a_client_secret')
|
56
|
+
```
|
57
|
+
后者创建的实例受全局配置影响
|
58
|
+
3. Baidu OAuth 有四种(当前实现两种)获取 Access Token 的方式,包括 `Authorization Code`, `Implicit Grant`, `Client Credentials`, `Device`以及一个刷新方式`Refresh Token`
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
client.code_flow.authorize_url # 使用 Authorization Code 授权流程
|
62
|
+
client.code_flow.get_token # 使用 Authorization Code 授权流程
|
63
|
+
client.device_flow.user_and_device_code # 使用 Device 授权流程
|
64
|
+
client.device_flow.get_token # 使用 Device 授权流程
|
65
|
+
client.refresh # Access Token刷新方式
|
66
|
+
```
|
67
|
+
4. `Baidu::Session` 包含授权信息
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
session = client.code_flow.get_token
|
71
|
+
session = client.device_flow.get_token
|
72
|
+
...
|
73
|
+
```
|
74
|
+
|
75
|
+
### Authorization Code 授权流程示例:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# 1. Edit and save this snippet as code.rb
|
79
|
+
# 2. Run by: ruby coce.rb
|
80
|
+
# 3. Visit: http://localhost:4567/auth
|
81
|
+
|
82
|
+
require 'sinatra'
|
83
|
+
require 'baidu/oauth'
|
84
|
+
|
85
|
+
Baidu.config do |c|
|
86
|
+
c.client_id = ENV['BAIDU_CLIENT_ID'] # Change to your client_id
|
87
|
+
c.client_secret = ENV['BAIDU_CLIENT_SECRET'] # Change to your client_secret
|
88
|
+
end
|
89
|
+
|
90
|
+
# 使用时需要到 http://developer.baidu.com/console 将此 callback 添加到应用安全设置中
|
91
|
+
callback_uri = 'http://localhost:4567/auth/callback'
|
92
|
+
|
93
|
+
get '/' do
|
94
|
+
'Hello world!'
|
95
|
+
end
|
96
|
+
|
97
|
+
get '/auth' do
|
98
|
+
client = Baidu::OAuth::Client.new
|
99
|
+
redirect client.code_flow.authorize_url(callback_uri, confirm_login: true)
|
100
|
+
end
|
101
|
+
|
102
|
+
get '/auth/callback' do
|
103
|
+
begin
|
104
|
+
client = Baidu::OAuth::Client.new
|
105
|
+
session = client.code_flow.get_token params[:code], callback_uri
|
106
|
+
"auth success: #{session.access_token}"
|
107
|
+
rescue => e
|
108
|
+
e.to_s
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
## PCS
|
114
|
+
要使用 PCS 必须到 [百度开发者中心](http://developer.baidu.com/console) 开启指定应用的 PCS API 权限,参考 [开通PCS API权限](http://developer.baidu.com/wiki/index.php?title=docs/pcs/guide/api_approve),并获取所设置的文件目录。
|
115
|
+
|
116
|
+
文件 API 已经完全实现。API 中涉及到的文件或目录路径,都是相对于开通 PCS API 权限时所设置的文件目录。比如:上传文件时需要指定文件保存路径,假设指定为 `/mydata/hi.png`,那么在 PCS 中实际的保存路径为 `/apps/文件目录/mydata/hi.png`。
|
117
|
+
|
118
|
+
1. 全局配置
|
119
|
+
```ruby
|
120
|
+
Baidu.config do |c|
|
121
|
+
# ...
|
122
|
+
c.pcs_dir_name = 'notes' # 文件目录名称
|
123
|
+
# ...
|
124
|
+
end
|
125
|
+
```
|
126
|
+
全局配置非强制性的,可以在创建 `Baidu::PCS::Client` 实例时设置
|
127
|
+
|
128
|
+
2. 创建 Baidu::PCS::Client 实例
|
129
|
+
```ruby
|
130
|
+
require 'baidu/pcs'
|
131
|
+
client = Baidu::PCS::Client.new('token_or_session')
|
132
|
+
# 或者
|
133
|
+
client = Baidu::PCS::Client.new('token_or_session', 'notes')
|
134
|
+
```
|
135
|
+
|
136
|
+
3. API 调用
|
137
|
+
```ruby
|
138
|
+
File.open('/opt/ubuntu-12.04.3-server-amd64.iso', 'r') do |f|
|
139
|
+
client.upload(f, block_upload: true)
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
## 异常处理
|
144
|
+
API 的异常被封装为 `Baidu::Errors::Error`,包含三个子类:
|
145
|
+
|
146
|
+
1. `Baidu::Errors::AuthError` # Access Token 过期或未授权
|
147
|
+
2. `Baidu::Errors::ClientError` # API 返回 `400+` 错误
|
148
|
+
3. `Baidu::Errors::ServerError` # API 返回 `500+` 错误
|
149
|
+
|
150
|
+
`Baidu::Errors::Error => e`,异常的具体信息可以通过如下方式获取到:
|
151
|
+
```ruby
|
152
|
+
begin
|
153
|
+
client.list('apitest/movies')
|
154
|
+
rescue Baidu::Errors::Error => e
|
155
|
+
puts e.code # 错误代码,详情参考百度开发者中心文档
|
156
|
+
puts e.msg # 错误描述
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
## Copyright
|
161
|
+
MIT License. Copyright (c) 2013 Lonre Wang.
|
data/Rakefile
ADDED
data/baidu-pcs.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'baidu/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "baidu-sdk"
|
9
|
+
spec.version = Baidu::VERSION
|
10
|
+
spec.authors = ["Lonre Wang"]
|
11
|
+
spec.email = ["me@wanglong.me"]
|
12
|
+
spec.description = %q{Unofficial Baidu REST api sdk for ruby, including OAuth, PCS, etc.}
|
13
|
+
spec.summary = %q{Unofficial Baidu REST API SDK for Ruby.}
|
14
|
+
spec.homepage = "https://github.com/lonre/baidu-sdk-ruby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_runtime_dependency "multipart-post", "~> 1.2"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Baidu
|
2
|
+
module Configure
|
3
|
+
# 申请创建应用后获取的 API Key
|
4
|
+
# @return [String]
|
5
|
+
attr_accessor :client_id
|
6
|
+
|
7
|
+
# 申请创建应用后获取的 Secret Key
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :client_secret
|
10
|
+
|
11
|
+
# 用来控制是否在终端输出调试信息,主要涉及网络请求部分(Net::HTTP)
|
12
|
+
# @note 请勿在生产环境中打开
|
13
|
+
# @return [Boolean] 默认为 false
|
14
|
+
attr_accessor :debug
|
15
|
+
|
16
|
+
# 通过 block 进行 API 配置
|
17
|
+
#
|
18
|
+
# @yield [c]
|
19
|
+
# @yieldparam c [Configure]
|
20
|
+
# @return [Configure]
|
21
|
+
def config
|
22
|
+
self.debug = false # 默认 false
|
23
|
+
yield self
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/baidu/core.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/https'
|
5
|
+
require 'uri'
|
6
|
+
require 'baidu/version'
|
7
|
+
require 'baidu/session'
|
8
|
+
require 'baidu/errors/error'
|
9
|
+
require 'baidu/support/util'
|
10
|
+
require 'baidu/support/request'
|
11
|
+
require 'baidu/configure'
|
12
|
+
|
13
|
+
module Baidu
|
14
|
+
|
15
|
+
class << self
|
16
|
+
include Baidu::Configure
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Baidu
|
2
|
+
module Errors
|
3
|
+
class Error < StandardError
|
4
|
+
attr_reader :msg, :code, :response
|
5
|
+
|
6
|
+
def initialize(msg, code=nil, response=nil)
|
7
|
+
@msg, @code, @response = msg, code, response
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
arr = %w[Error]
|
12
|
+
arr << "code: #{code}" unless code.nil?
|
13
|
+
arr << "msg: #{msg}" unless msg.nil?
|
14
|
+
arr.join(' ')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class AuthError < Error; end
|
19
|
+
class ClientError < Error; end
|
20
|
+
class ServerError < Error; end
|
21
|
+
end
|
22
|
+
end
|
data/lib/baidu/oauth.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'baidu/core'
|
2
|
+
require 'baidu/oauth/client'
|
3
|
+
|
4
|
+
module Baidu
|
5
|
+
module OAuth
|
6
|
+
SITE = 'https://openapi.baidu.com'
|
7
|
+
AUTHORIZATION_ENDPOINT = '/oauth/2.0/authorize'
|
8
|
+
TOKEN_ENDPOINT = '/oauth/2.0/token'
|
9
|
+
DEVICE_ENDPOINT = '/oauth/2.0/device/code'
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'baidu/oauth/flow/code'
|
4
|
+
require 'baidu/oauth/flow/device'
|
5
|
+
|
6
|
+
module Baidu
|
7
|
+
module OAuth
|
8
|
+
class Client
|
9
|
+
include Baidu::Support::Request
|
10
|
+
|
11
|
+
# 申请创建应用后获取的 API Key
|
12
|
+
# @return [String]
|
13
|
+
attr_accessor :client_id
|
14
|
+
# 申请创建应用后获取的 Secret Key
|
15
|
+
# @return [String]
|
16
|
+
attr_accessor :client_secret
|
17
|
+
# @private
|
18
|
+
attr_reader :site
|
19
|
+
|
20
|
+
# 创建一个 OAuth API 实例
|
21
|
+
#
|
22
|
+
# @example 如果不使用全局配置,则可以在创建新实例时指定 +client_id+ 和 +client_secret+
|
23
|
+
# client = Baidu::OAuth::Client.new('a_client_id', 'a_client_secret')
|
24
|
+
def initialize(client_id=Baidu.client_id, client_secret=Baidu.client_secret)
|
25
|
+
@client_id = client_id
|
26
|
+
@client_secret = client_secret
|
27
|
+
@site = Baidu::OAuth::SITE
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!method code_flow
|
31
|
+
# 采用 Authorization Code 获取 Access Token 的授权验证流程
|
32
|
+
# @return [Flow::Code]
|
33
|
+
# @!method device_flow
|
34
|
+
# 采用 Device Code 获取 Access Token 的授权验证流程
|
35
|
+
# @return [Flow::Device]
|
36
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/authorization Authorization Code
|
37
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/device Device
|
38
|
+
[:code, :device].each do |flow|
|
39
|
+
define_method("#{flow}_flow".to_sym) do
|
40
|
+
Baidu::OAuth::Flow.const_get(flow.capitalize).new self
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# 刷新 Access Token
|
45
|
+
#
|
46
|
+
# @param [String] token 用于刷新 Access Token 用的 Refresh Token
|
47
|
+
# @option params [String] :scope 以空格分隔的权限列表,若不传递此参数,代表请求的数据访问操作权限与上次获取 Access Token 时一致。
|
48
|
+
# 通过 Refresh Token 刷新 Access Token 时所要求的scope权限范围必须小于等于上次获取 Access Token 时授予的权限范围。
|
49
|
+
# 关于权限的具体信息请参考“{http://developer.baidu.com/wiki/index.php?title=docs/oauth/list 权限列表}”
|
50
|
+
# @return [Baidu::Session]
|
51
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/list 权限列表
|
52
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/overview Access Token 生命周期
|
53
|
+
def refresh(token, params={})
|
54
|
+
body = {
|
55
|
+
grant_type: 'refresh_token',
|
56
|
+
refresh_token: token,
|
57
|
+
client_id: self.client_id,
|
58
|
+
client_secret: self.client_secret
|
59
|
+
}.update params
|
60
|
+
rest = post Baidu::OAuth::TOKEN_ENDPOINT, nil, body
|
61
|
+
return nil if rest.nil?
|
62
|
+
Baidu::Session.from rest
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|