tdlib-ruby 0.2.0 → 0.3.0
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 +5 -5
- data/.travis.yml +29 -3
- data/ChangeLog.md +9 -5
- data/README.md +1 -1
- data/lib/tdlib/api.rb +17 -12
- data/lib/tdlib/client.rb +7 -11
- data/lib/tdlib/errors.rb +3 -0
- data/lib/tdlib/version.rb +1 -1
- data/spec/integration/tdlib_spec.rb +65 -0
- data/spec/tdlib_spec.rb +0 -131
- data/tdlib-ruby.gemspec +3 -2
- metadata +24 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2e11ed2e37f6949476c1b50f69f754692ff91fb
|
4
|
+
data.tar.gz: 25639d93e2f8d29e0ef3283dcddd3b31a8e1fbb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 748462dfb42dc3ae79dbc2099af326270184a41641c9545961650f868088d3fe3b0cf02cd626ad3f1511511d0311f52088fdba8a54e7060d1ddfcd55bdea5e3b
|
7
|
+
data.tar.gz: c31362d10969c1eba778bb63fc62526c6ed80b73afc57309adcedd3896dab81914bc67dd4499818eaa919f10a4e5fbd6a1b8f0e5cdb39d03e0ec844ffbc8abae
|
data/.travis.yml
CHANGED
@@ -1,5 +1,31 @@
|
|
1
|
-
---
|
2
1
|
language: ruby
|
3
2
|
rvm:
|
4
|
-
|
5
|
-
|
3
|
+
- 2.3.6
|
4
|
+
- 2.5.0
|
5
|
+
addons:
|
6
|
+
apt:
|
7
|
+
sources:
|
8
|
+
- ubuntu-toolchain-r-test
|
9
|
+
- llvm-toolchain-precise-3.8
|
10
|
+
packages:
|
11
|
+
- g++-5
|
12
|
+
- clang-3.8
|
13
|
+
- libc++-dev
|
14
|
+
- libc++abi-dev
|
15
|
+
- gperf
|
16
|
+
env:
|
17
|
+
matrix:
|
18
|
+
- CXX=clang++-3.8 CC=clang-3.8
|
19
|
+
global:
|
20
|
+
secure: DmAoRsr+dTPVNs486nBXzChjzCCVdoxfmSbvqMBBl0v8uuUIZD1rSfYKcEaL5pmE7vgYq3X+3Dk0gf4ef/p2xPVKqFKrkBiF/7t06WgQTH7003gHMLq3aC7R9+1xvqJDzpoc6UB59Y1fOJBe5YfqDuw1dT9a46tzY2uzoVpEpbN8M/hssaKf6Wuzvpz5yekFeXq2raPwi3aOqvkSG+ODoacVdYQgJ4Vn0//CI2HzWijkQCvdsefWQUKkbgtuLWRp3UNy8AEhVWzQnTcSM7oc+MwMsOI/90DZkTP5n2WJl/CFTM5b70VyrIciG92SvTAhhBo/p7t3QBJa6kJMPXAHh5q+3wqVQA411+CoVF48bO5rKjKY3Ply49uqAzVJRh+Tkhf5uC1pHiZ6QKGxu1Czde+mKItBpaDmJFmQi0CSdv2WYXLnJWOQIO6vc2P3liwpMDRDyaGWOQtUYS+gHAlRbD4NPycIkGjkcjmLMqSEEO1TdCpM+CYwTvNTkRS+9HrWXZNdEfWORDYHgYohoIP6kY6XWSgunUb6F6pVxLoPWJNEBEVuIMZeOa/s9oklxBzD5XXIzBx4QsPYanvfxoN1uOcXlBXbOGqwiqb+urokNDw+BzWhbA+xY03U2+yO0Ujh3HQDyMDtrXEQfaPC0SxpEvIINVYwznG4sMKvbOaCVSo=
|
21
|
+
before_install:
|
22
|
+
- set -e
|
23
|
+
- git clone https://github.com/tdlib/td
|
24
|
+
- cd td
|
25
|
+
- mkdir -p build
|
26
|
+
- cd build
|
27
|
+
- cmake -DCMAKE_BUILD_TYPE=Release ..
|
28
|
+
- cmake --build . -- -j 4
|
29
|
+
cache:
|
30
|
+
directories:
|
31
|
+
- $HOME/.ccache
|
data/ChangeLog.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
-
### 0.
|
1
|
+
### 0.3.0 / 2018-02-16
|
2
2
|
|
3
|
-
*
|
3
|
+
* Use Concurrent::Promise instead of timeout module
|
4
|
+
* Add integration tests
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
### 0.2.0 2018-02-16
|
6
|
+
### 0.2.0 / 2018-02-16
|
8
7
|
|
9
8
|
* Improved lib path detection
|
10
9
|
|
11
10
|
* TD::Client#on_ready method
|
12
11
|
|
12
|
+
### 0.1.0 / 2018-02-01
|
13
|
+
|
14
|
+
* Initial release:
|
15
|
+
|
16
|
+
Basic featues
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# tdlib-ruby
|
2
2
|
|
3
|
-
[](https://travis-ci.org/centosadmin/tdlib-ruby)
|
3
|
+
[](https://codeclimate.com/github/centosadmin/tdlib-ruby/maintainability) [](https://travis-ci.org/centosadmin/tdlib-ruby)
|
4
4
|
|
5
5
|
## Description
|
6
6
|
|
data/lib/tdlib/api.rb
CHANGED
@@ -36,21 +36,26 @@ module TD::Api
|
|
36
36
|
module Dl
|
37
37
|
extend Fiddle::Importer
|
38
38
|
|
39
|
+
@mutex = Mutex.new
|
40
|
+
|
39
41
|
module_function
|
40
42
|
|
41
43
|
def method_missing(method_name, *args)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
44
|
+
@mutex.synchronize do
|
45
|
+
return if respond_to?(method_name)
|
46
|
+
dlload(find_lib)
|
47
|
+
|
48
|
+
extern 'void* td_json_client_create()'
|
49
|
+
extern 'void* td_json_client_send(void*, char*)'
|
50
|
+
extern 'char* td_json_client_receive(void*, double)'
|
51
|
+
extern 'char* td_json_client_execute(void*, char*)'
|
52
|
+
extern 'void td_set_log_verbosity_level(int)'
|
53
|
+
extern 'void td_json_client_destroy(void*)'
|
54
|
+
extern 'void td_set_log_file_path(char*)'
|
55
|
+
|
56
|
+
undef method_missing
|
57
|
+
public_send(method_name, *args)
|
58
|
+
end
|
54
59
|
end
|
55
60
|
|
56
61
|
def find_lib
|
data/lib/tdlib/client.rb
CHANGED
@@ -58,6 +58,8 @@
|
|
58
58
|
#
|
59
59
|
# p @me
|
60
60
|
class TD::Client
|
61
|
+
include Concurrent
|
62
|
+
|
61
63
|
TIMEOUT = 20
|
62
64
|
|
63
65
|
def initialize(td_client = TD::Api.client_create,
|
@@ -96,14 +98,11 @@ class TD::Client
|
|
96
98
|
@update_manager.add_handler(handler)
|
97
99
|
query['@extra'] = extra
|
98
100
|
TD::Api.client_send(@td_client, query)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
@update_manager.remove_handler(handler)
|
103
|
-
return result
|
104
|
-
end
|
105
|
-
end
|
101
|
+
promise = Promise.new do
|
102
|
+
until result do end
|
103
|
+
result
|
106
104
|
end
|
105
|
+
promise.execute.value(TIMEOUT) || (raise TD::TimeoutError)
|
107
106
|
end
|
108
107
|
|
109
108
|
# Synchronously executes TDLib request
|
@@ -131,10 +130,7 @@ class TD::Client
|
|
131
130
|
end
|
132
131
|
|
133
132
|
def on_ready(&_)
|
134
|
-
|
135
|
-
until @ready do
|
136
|
-
end
|
137
|
-
end
|
133
|
+
raise TD::TimeoutError unless Promise.execute { until @ready do end }.then { self }.execute.value(TIMEOUT)
|
138
134
|
yield self
|
139
135
|
end
|
140
136
|
|
data/lib/tdlib/errors.rb
CHANGED
data/lib/tdlib/version.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tdlib-ruby'
|
3
|
+
|
4
|
+
describe TD::Client do
|
5
|
+
let(:client) { TD::Client.new }
|
6
|
+
let(:payload) { { '@type': 'getTextEntities', 'text': '@telegram' } }
|
7
|
+
|
8
|
+
before do
|
9
|
+
TD.configure do |config|
|
10
|
+
config.lib_path = File.join(File.expand_path("#{TD.root_path}/../"), 'td', 'build')
|
11
|
+
|
12
|
+
config.client.api_id = ENV['TD_API_ID']
|
13
|
+
config.client.api_hash = ENV['TD_API_HASH']
|
14
|
+
config.client.use_test_dc = true
|
15
|
+
end
|
16
|
+
|
17
|
+
TD::Api.set_log_verbosity_level(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#on_ready' do
|
21
|
+
subject { client.on_ready { [client, 'ready'] } }
|
22
|
+
|
23
|
+
it { is_expected.to include(client) }
|
24
|
+
it { is_expected.to include('ready') }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#broadcast' do
|
28
|
+
context 'when no block given' do
|
29
|
+
subject { client.on_ready { client.broadcast(payload) } }
|
30
|
+
|
31
|
+
it { expect { subject }.not_to raise_error(Exception) }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when block given' do
|
35
|
+
subject { client.on_ready { client.broadcast(payload) { |update| @result = update } } }
|
36
|
+
|
37
|
+
it 'runs block on update' do
|
38
|
+
subject
|
39
|
+
sleep 3
|
40
|
+
expect(@result).to include('@type', 'entities')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#broadcast_and_receive' do
|
46
|
+
subject { client.on_ready { client.broadcast_and_receive(payload ) } }
|
47
|
+
|
48
|
+
it { is_expected.to include('@type', 'entities') }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#on' do
|
52
|
+
subject do
|
53
|
+
client.on_ready do
|
54
|
+
client.on('textEntities') { |update| @result = update }
|
55
|
+
client.broadcast(payload)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'runs block on update' do
|
60
|
+
subject
|
61
|
+
sleep 3
|
62
|
+
expect(@result).to include('@type', 'entities')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/spec/tdlib_spec.rb
CHANGED
@@ -6,134 +6,3 @@ describe TD do
|
|
6
6
|
expect(subject.const_get('VERSION')).to_not be_empty
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
10
|
-
class Base
|
11
|
-
def self.inherited(subclass)
|
12
|
-
subclass.prepend(Module.new do
|
13
|
-
define_method(:call) do |*args|
|
14
|
-
begin
|
15
|
-
super(*args)
|
16
|
-
ensure
|
17
|
-
p 'test1'
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class Test < Base
|
25
|
-
def call(a)
|
26
|
-
p a
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
p Test.new.('aaa')
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
TD.configure do |config|
|
35
|
-
config.lib_path = Dir.home
|
36
|
-
|
37
|
-
config.client.api_id = 39991
|
38
|
-
config.client.api_hash = '89b13f68e3840ac787eaa9ba8236d983'
|
39
|
-
end
|
40
|
-
|
41
|
-
#raise TD.config.lib_path
|
42
|
-
|
43
|
-
# TD::Api.set_log_verbosity_level(1)
|
44
|
-
#TD::Api.set_log_file_path("#{Dir.home}/tdlib.log")
|
45
|
-
|
46
|
-
client = TD::Client.new
|
47
|
-
|
48
|
-
begin
|
49
|
-
state = nil
|
50
|
-
|
51
|
-
client.on('updateAuthorizationState') do |update|
|
52
|
-
next unless update.dig('authorization_state', '@type') == 'authorizationStateWaitPhoneNumber'
|
53
|
-
state = :wait_phone
|
54
|
-
end
|
55
|
-
|
56
|
-
client.on('updateAuthorizationState') do |update|
|
57
|
-
next unless update.dig('authorization_state', '@type') == 'authorizationStateWaitCode'
|
58
|
-
state = :wait_code
|
59
|
-
end
|
60
|
-
|
61
|
-
client.on('updateAuthorizationState') do |update|
|
62
|
-
next unless update.dig('authorization_state', '@type') == 'authorizationStateReady'
|
63
|
-
state = :ready
|
64
|
-
end
|
65
|
-
|
66
|
-
loop do
|
67
|
-
case state
|
68
|
-
when :wait_phone
|
69
|
-
p 'Please, enter your phone number:'
|
70
|
-
phone = STDIN.gets.strip
|
71
|
-
params = {
|
72
|
-
'@type' => 'setAuthenticationPhoneNumber',
|
73
|
-
'phone_number' => phone
|
74
|
-
}
|
75
|
-
client.broadcast_and_receive(params)
|
76
|
-
when :wait_code
|
77
|
-
p 'Please, enter code from SMS:'
|
78
|
-
code = STDIN.gets.strip
|
79
|
-
params = {
|
80
|
-
'@type' => 'checkAuthenticationCode',
|
81
|
-
'code' => code
|
82
|
-
}
|
83
|
-
a = client.broadcast_and_receive(params)
|
84
|
-
when :ready
|
85
|
-
client.on_ready do |client|
|
86
|
-
@me = client.broadcast_and_receive('@type' => 'getMe')
|
87
|
-
#@contacts = client.broadcast_and_receive('@type' => 'searchChatsOnServer', 'query' => 'BotFather', 'limit' => 25)
|
88
|
-
@user = client.broadcast_and_receive('@type' => 'getUser', user_id: 132916567)
|
89
|
-
@chat = client.broadcast_and_receive('@type' => 'createNewBasicGroupChat', user_ids: [132916567], title: '111')
|
90
|
-
@link = client.broadcast_and_receive('@type' => 'generateChatInviteLink', 'chat_id' => @chat['id'])
|
91
|
-
@chat2 = client.broadcast_and_receive('@type' => 'getBasicGroupFullInfo', 'basic_group_id' => @chat.dig('type', 'basic_group_id'))['members'].map { |m| m['user_id'] }
|
92
|
-
@toggle_admins= client.broadcast_and_receive('@type' => 'toggleBasicGroupAdministrators', 'basic_group_id' => @chat.dig('type', 'basic_group_id'), everyone_is_administrator: false)
|
93
|
-
@set_st = client.broadcast_and_receive('@type' => 'setChatMemberStatus',
|
94
|
-
'chat_id' => @chat['id'],
|
95
|
-
'user_id' => 132916567,
|
96
|
-
'status' => {'@type' => 'chatMemberStatusAdministrator'})
|
97
|
-
@del = client.broadcast_and_receive('@type' => 'setChatMemberStatus',
|
98
|
-
'chat_id' => @chat['id'],
|
99
|
-
'user_id' => @me['id'],
|
100
|
-
'status' => {'@type' => 'chatMemberStatusLeft'})
|
101
|
-
@del1 = client.broadcast_and_receive('@type' => 'setChatMemberStatus',
|
102
|
-
'chat_id' => @chat['id'],
|
103
|
-
'user_id' => 132916567,
|
104
|
-
'status' => {'@type' => 'chatMemberStatusLeft'})
|
105
|
-
# @chat = client.broadcast_and_receive('@type' => 'createNewSupergroupChat', 'title' => 'Test3')
|
106
|
-
# @link = client.broadcast_and_receive('@type' => 'generateChatInviteLink', 'chat_id' => @chat['id'])
|
107
|
-
# @chat2 = client.broadcast_and_receive('@type' => 'getChat', 'chat_id' => @chat['id'])
|
108
|
-
# admin = true
|
109
|
-
# status = { '@type' => (admin ? 'chatMemberStatusAdministrator' : 'chatMemberStatusMember'),
|
110
|
-
# 'can_edit_messages' => true,
|
111
|
-
# 'can_post_messages' => true,
|
112
|
-
# 'can_delete_messages' => true }
|
113
|
-
# @add_u = client.broadcast_and_receive('@type' => 'addChatMember',
|
114
|
-
# 'chat_id' => @chat['id'],
|
115
|
-
# 'user_id' => @user['id'])
|
116
|
-
# @set_st = client.broadcast_and_receive('@type' => 'setChatMemberStatus',
|
117
|
-
# 'chat_id' => @chat['id'],
|
118
|
-
# 'user_id' => @user['id'],
|
119
|
-
# 'status' => status)
|
120
|
-
# supergroup_id = @chat2.dig('type', 'supergroup_id')
|
121
|
-
# #client.broadcast_and_receive('@type' => 'deleteSupergroup', 'supergroup_id' => supergroup_id)
|
122
|
-
end
|
123
|
-
break
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
ensure
|
128
|
-
client.close
|
129
|
-
end
|
130
|
-
p @me
|
131
|
-
p @chat
|
132
|
-
p @link
|
133
|
-
p @chat2
|
134
|
-
p @toggle_admins
|
135
|
-
p @add_u
|
136
|
-
p @set_st
|
137
|
-
p @del
|
138
|
-
p @del1
|
139
|
-
# p @chat
|
data/tdlib-ruby.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.name = "tdlib-ruby"
|
9
9
|
gem.version = TD::VERSION
|
10
10
|
gem.summary = 'Ruby bindings and client for TDlib'
|
11
|
-
gem.description =
|
11
|
+
gem.description = 'Ruby bindings and client for TDlib'
|
12
12
|
gem.license = "MIT"
|
13
13
|
gem.authors = ["Southbridge"]
|
14
14
|
gem.email = "ask@southbridge.io"
|
@@ -30,9 +30,10 @@ Gem::Specification.new do |gem|
|
|
30
30
|
gem.require_paths = ['lib']
|
31
31
|
|
32
32
|
gem.add_runtime_dependency 'dry-configurable', '~> 0.7'
|
33
|
+
gem.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
33
34
|
|
34
35
|
gem.add_development_dependency 'bundler', '~> 1.10'
|
35
|
-
gem.add_development_dependency 'rake', '
|
36
|
+
gem.add_development_dependency 'rake', '12.3.1'
|
36
37
|
gem.add_development_dependency 'rspec', '~> 3.0'
|
37
38
|
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
38
39
|
gem.add_development_dependency 'yard', '~> 0.9'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tdlib-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Southbridge
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-configurable
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: concurrent-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,16 +56,16 @@ dependencies:
|
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- -
|
59
|
+
- - '='
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
61
|
+
version: 12.3.1
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- -
|
66
|
+
- - '='
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
68
|
+
version: 12.3.1
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,7 +122,7 @@ dependencies:
|
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '0.11'
|
111
|
-
description:
|
125
|
+
description: Ruby bindings and client for TDlib
|
112
126
|
email: ask@southbridge.io
|
113
127
|
executables: []
|
114
128
|
extensions: []
|
@@ -131,6 +145,7 @@ files:
|
|
131
145
|
- lib/tdlib/update_manager.rb
|
132
146
|
- lib/tdlib/utils.rb
|
133
147
|
- lib/tdlib/version.rb
|
148
|
+
- spec/integration/tdlib_spec.rb
|
134
149
|
- spec/spec_helper.rb
|
135
150
|
- spec/support/update_manager_stub.rb
|
136
151
|
- spec/tdlib_spec.rb
|
@@ -156,11 +171,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
156
171
|
version: '0'
|
157
172
|
requirements: []
|
158
173
|
rubyforge_project:
|
159
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.6.14
|
160
175
|
signing_key:
|
161
176
|
specification_version: 4
|
162
177
|
summary: Ruby bindings and client for TDlib
|
163
178
|
test_files:
|
179
|
+
- spec/integration/tdlib_spec.rb
|
164
180
|
- spec/spec_helper.rb
|
165
181
|
- spec/support/update_manager_stub.rb
|
166
182
|
- spec/tdlib_spec.rb
|