bitflyer 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7e97040225bbcb8c03f0b30d2cc7e99f961b26b8
4
- data.tar.gz: 635e649a96a37e01cc7c7adb6cdc199cea09cc41
2
+ SHA256:
3
+ metadata.gz: 96469fe331a89e6551b43e7224b0eb9dbe7e21a5f6c6811068d1fcbdc6d73472
4
+ data.tar.gz: 4480a1a3aa087333adf60e31b60c90d568a403b43acd348aac0d60c3f8de1c65
5
5
  SHA512:
6
- metadata.gz: 5c2169cd4ee452ecdef914b95c35d0d01a42f9dc4cf55e2b32dce3f741da641d3684b0aa049e5a5db4b9700c7ab464b1c88b7bcde89e2b39c368570ba4e73e8e
7
- data.tar.gz: c678a6b2ca82f303ec2d7c02c192bde247e144328e2feed5b3d3e9dac813f66ce638664cf38ffbca3f5d75789bdef6559fe459132b2b95a8d36296d6d7b7cda9
6
+ metadata.gz: 21ad55e0aedf232c88aa20ed7bc49ebe5f3a92acff3dfe18772bb4d4b6576f07f7fad79926372f6fe2ca840175064b75fef6db767e246d882d4624845082bd9c
7
+ data.tar.gz: 95570ef23a9d6a6abb4667406a7fe19b41e81ec0b3e0cfd433d1d77b2df9a3f2224616eb5c4ae92abcc8ede4769953cd7c060ec0e7da4b5d35bc9ca6ab7e156f
@@ -1,74 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bitflyer (0.2.0)
4
+ bitflyer (0.2.1)
5
5
  faraday (~> 0.14.0)
6
6
  faraday_middleware (~> 0.12.0)
7
- pubnub (~> 4.0.22)
7
+ websocket-client-simple (~> 0.3.0)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- celluloid (0.17.3)
13
- celluloid-essentials
14
- celluloid-extras
15
- celluloid-fsm
16
- celluloid-pool
17
- celluloid-supervision
18
- timers (>= 4.1.1)
19
- celluloid-essentials (0.20.5)
20
- timers (>= 4.1.1)
21
- celluloid-extras (0.20.5)
22
- timers (>= 4.1.1)
23
- celluloid-fsm (0.20.5)
24
- timers (>= 4.1.1)
25
- celluloid-pool (0.20.5)
26
- timers (>= 4.1.1)
27
- celluloid-supervision (0.20.6)
28
- timers (>= 4.1.1)
29
- concurrent-ruby (1.0.5)
30
12
  diff-lcs (1.3)
31
- dry-configurable (0.7.0)
32
- concurrent-ruby (~> 1.0)
33
- dry-container (0.6.0)
34
- concurrent-ruby (~> 1.0)
35
- dry-configurable (~> 0.1, >= 0.1.3)
36
- dry-core (0.4.2)
37
- concurrent-ruby (~> 1.0)
38
- dry-equalizer (0.2.0)
39
- dry-logic (0.4.2)
40
- dry-container (~> 0.2, >= 0.2.6)
41
- dry-core (~> 0.2)
42
- dry-equalizer (~> 0.2)
43
- dry-types (0.12.2)
44
- concurrent-ruby (~> 1.0)
45
- dry-configurable (~> 0.1)
46
- dry-container (~> 0.3)
47
- dry-core (~> 0.2, >= 0.2.1)
48
- dry-equalizer (~> 0.2)
49
- dry-logic (~> 0.4, >= 0.4.2)
50
- inflecto (~> 0.0.0, >= 0.0.2)
51
- dry-validation (0.11.1)
52
- concurrent-ruby (~> 1.0)
53
- dry-configurable (~> 0.1, >= 0.1.3)
54
- dry-core (~> 0.2, >= 0.2.1)
55
- dry-equalizer (~> 0.2)
56
- dry-logic (~> 0.4, >= 0.4.0)
57
- dry-types (~> 0.12.0)
13
+ event_emitter (0.2.6)
58
14
  faraday (0.14.0)
59
15
  multipart-post (>= 1.2, < 3)
60
16
  faraday_middleware (0.12.2)
61
17
  faraday (>= 0.7.4, < 1.0)
62
- hitimes (1.2.6)
63
- httpclient (2.8.3)
64
- inflecto (0.0.2)
65
- json (2.1.0)
66
18
  multipart-post (2.0.0)
67
- pubnub (4.0.27)
68
- celluloid (~> 0.17)
69
- dry-validation (~> 0.10)
70
- httpclient (~> 2.8, >= 2.8.3)
71
- json (>= 1.8, < 3)
72
19
  rake (12.3.0)
73
20
  rspec (3.7.0)
74
21
  rspec-core (~> 3.7.0)
@@ -83,8 +30,10 @@ GEM
83
30
  diff-lcs (>= 1.2.0, < 2.0)
84
31
  rspec-support (~> 3.7.0)
85
32
  rspec-support (3.7.1)
86
- timers (4.1.2)
87
- hitimes
33
+ websocket (1.2.8)
34
+ websocket-client-simple (0.3.0)
35
+ event_emitter
36
+ websocket
88
37
 
89
38
  PLATFORMS
90
39
  ruby
@@ -96,4 +45,4 @@ DEPENDENCIES
96
45
  rspec
97
46
 
98
47
  BUNDLED WITH
99
- 1.15.4
48
+ 1.17.2
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_dependency 'faraday', '~> 0.14.0'
21
21
  spec.add_dependency 'faraday_middleware', '~> 0.12.0'
22
- spec.add_dependency 'pubnub', '~> 4.0.22'
22
+ spec.add_dependency 'websocket-client-simple', '~> 0.3.0'
23
23
  spec.add_development_dependency 'bundler', '~> 1.12'
24
24
  spec.add_development_dependency 'rake'
25
25
  spec.add_development_dependency 'rspec'
@@ -1,8 +1,7 @@
1
- require 'pubnub'
1
+ require_relative './websocket'
2
2
 
3
3
  module Bitflyer
4
4
  module Realtime
5
- PUBNUB_SUBSCRIBE_KEY = 'sub-c-52a9ab50-291b-11e5-baaa-0619f8945a4f'.freeze
6
5
  CHANNEL_NAMES = [
7
6
  'lightning_board_snapshot_BTC_JPY',
8
7
  'lightning_board_snapshot_FX_BTC_JPY',
@@ -22,25 +21,20 @@ module Bitflyer
22
21
  'lightning_executions_BCH_BTC'
23
22
  ].freeze
24
23
 
25
- class Client
26
- attr_accessor *Realtime::CHANNEL_NAMES.map { |name| name.gsub('lightning_', '').downcase.to_sym }
24
+ SOCKET_HOST = 'https://io.lightstream.bitflyer.com'
27
25
 
28
- def initialize
29
- @pubnub = Pubnub.new(subscribe_key: Realtime::PUBNUB_SUBSCRIBE_KEY)
26
+ class Client
27
+ attr_accessor :websocket_client, :ping_interval, :ping_timeout, :last_ping_at, :last_pong_at
30
28
 
31
- @callback = Pubnub::SubscribeCallback.new(
32
- message: ->(envelope) {
33
- channel_name = envelope.result[:data][:subscribed_channel].gsub('lightning_', '').downcase.to_sym
34
- message = envelope.result[:data][:message]
35
- send(channel_name).call(message) if send(channel_name)
36
- },
37
- presence: ->(envelope) {},
38
- status: ->(envelope) {}
39
- )
29
+ Realtime::CHANNEL_NAMES.each do |channel_name|
30
+ define_method "#{channel_name.gsub('lightning_', '').downcase.to_sym}=" do |callback|
31
+ @websocket_client.subscribe(channel_name: channel_name.to_sym, &callback)
32
+ end
33
+ end
40
34
 
41
- @pubnub.add_listener(callback: @callback)
42
- @pubnub.subscribe(channels: Realtime::CHANNEL_NAMES)
35
+ def initialize
36
+ @websocket_client = Bitflyer::Realtime::WebSocketClient.new(host: SOCKET_HOST)
43
37
  end
44
38
  end
45
39
  end
46
- end
40
+ end
@@ -0,0 +1,136 @@
1
+ require 'websocket-client-simple'
2
+
3
+ module Bitflyer
4
+ module Realtime
5
+ class WebSocketClient
6
+ attr_accessor :websocket_client, :channel_name, :channel_callbacks, :ping_interval, :ping_timeout,
7
+ :last_ping_at, :last_pong_at, :error
8
+
9
+ def initialize(host:, debug: false)
10
+ @host = host
11
+ @debug = debug
12
+ @error = nil
13
+ @channel_names = []
14
+ @channel_callbacks = {}
15
+ connect
16
+ end
17
+
18
+ def subscribe(channel_name:, &block)
19
+ debug_log "Subscribe #{channel_name}"
20
+ @channel_names = (@channel_names + [channel_name]).uniq
21
+ @channel_callbacks[channel_name] = block
22
+ websocket_client.send "42#{['subscribe', channel_name].to_json}"
23
+ end
24
+
25
+ def connect
26
+ @websocket_client = WebSocket::Client::Simple.connect "#{@host}/socket.io/?transport=websocket"
27
+ this = self
28
+
29
+ Thread.new do
30
+ loop do
31
+ sleep 1
32
+ if @websocket_client && @websocket_client.open?
33
+ send_ping
34
+ wait_pong
35
+ end
36
+ end
37
+ end
38
+
39
+ Thread.new do
40
+ loop do
41
+ sleep 1
42
+ next unless @error
43
+ reconnect
44
+ end
45
+ end
46
+
47
+ @websocket_client.on(:message) { |payload| this.handle_message(payload: payload) }
48
+ @websocket_client.on(:error) { |error| this.handle_error(error: error) }
49
+ end
50
+
51
+ def send_ping
52
+ return unless @last_ping_at && @ping_interval
53
+ return unless Time.now.to_i - @last_ping_at > @ping_interval / 1000
54
+
55
+ debug_log 'Sent ping'
56
+ @websocket_client.send "2"
57
+ @last_ping_at = Time.now.to_i
58
+ end
59
+
60
+ def wait_pong
61
+ return unless @last_pong_at && @ping_timeout
62
+ return unless Time.now.to_i - @last_pong_at > @ping_timeout / 1000
63
+
64
+ debug_log 'Timed out waiting pong'
65
+ @websocket_client.close
66
+ end
67
+
68
+ def reconnect
69
+ return unless @error
70
+ debug_log 'Reconnecting...'
71
+
72
+ @error = nil
73
+ @websocket_client.close if @websocket_client.open?
74
+ connect
75
+ @channel_names.each do |channel_name|
76
+ debug_log "42#{{ subscribe: channel_name }.to_json}"
77
+ websocket_client.send "42#{['subscribe', channel_name].to_json}"
78
+ end
79
+ end
80
+
81
+ def handle_error(error:)
82
+ debug_log error
83
+ return unless error.kind_of? Errno::ECONNRESET
84
+ reconnect
85
+ end
86
+
87
+ def handle_message(payload:)
88
+ debug_log payload.data
89
+ return unless payload.data =~ /^\d+/
90
+ code, body = payload.data.scan(/^(\d+)(.*)$/)[0]
91
+
92
+ case code.to_i
93
+ when 0 then setup_by_response(json: body)
94
+ when 3 then receive_pong
95
+ when 41 then disconnect
96
+ when 42 then emit_message(json: body)
97
+ end
98
+ rescue => e
99
+ puts e
100
+ puts e.backtrace.join("\n")
101
+ end
102
+
103
+ def setup_by_response(json:)
104
+ body = JSON.parse json
105
+ @ping_interval = body["pingInterval"].to_i || 25000
106
+ @ping_timeout = body["pingTimeout"].to_i || 60000
107
+ @last_ping_at = Time.now.to_i
108
+ @last_pong_at = Time.now.to_i
109
+ channel_callbacks.each do |channel_name, _|
110
+ websocket_client.send "42#{['subscribe', channel_name].to_json}"
111
+ end
112
+ end
113
+
114
+ def receive_pong
115
+ debug_log 'Received pong'
116
+ @last_pong_at = Time.now.to_i
117
+ end
118
+
119
+ def disconnect
120
+ debug_log 'Disconnecting from server...'
121
+ @error = true
122
+ end
123
+
124
+ def emit_message(json:)
125
+ channel_name, *messages = JSON.parse json
126
+ return unless channel_name
127
+ messages.each { |message| @channel_callbacks[channel_name.to_sym]&.call(message) }
128
+ end
129
+
130
+ def debug_log(message)
131
+ return unless @debug
132
+ p message
133
+ end
134
+ end
135
+ end
136
+ end
@@ -1,3 +1,3 @@
1
1
  module Bitflyer
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitflyer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuji Ueki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-30 00:00:00.000000000 Z
11
+ date: 2019-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -39,19 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.12.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: pubnub
42
+ name: websocket-client-simple
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 4.0.22
47
+ version: 0.3.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 4.0.22
54
+ version: 0.3.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +114,7 @@ files:
114
114
  - lib/bitflyer/http/public.rb
115
115
  - lib/bitflyer/realtime.rb
116
116
  - lib/bitflyer/realtime/client.rb
117
+ - lib/bitflyer/realtime/websocket.rb
117
118
  - lib/bitflyer/version.rb
118
119
  homepage: https://github.com/unhappychoice/bitflyer
119
120
  licenses:
@@ -135,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
136
  version: '0'
136
137
  requirements: []
137
138
  rubyforge_project:
138
- rubygems_version: 2.6.11
139
+ rubygems_version: 2.7.6
139
140
  signing_key:
140
141
  specification_version: 4
141
142
  summary: Bitflyer API wrapper