internet_scrabble_club 0.1.1 → 0.2.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 +4 -4
- data/.gitignore +2 -0
- data/.ruby-version +1 -1
- data/Rakefile +1 -1
- data/bin/scraper.rb +12 -0
- data/bin/spectator.rb +91 -0
- data/internet_scrabble_club.gemspec +3 -4
- data/lib/internet_scrabble_club.rb +1 -1
- data/lib/internet_scrabble_club/client.rb +34 -30
- data/lib/internet_scrabble_club/client/callback_queue.rb +22 -0
- data/lib/internet_scrabble_club/client/middleware.rb +7 -4
- data/lib/internet_scrabble_club/client/middleware/request/echo_ping.rb +23 -0
- data/lib/internet_scrabble_club/client/middleware/request/keep_alive.rb +31 -0
- data/lib/internet_scrabble_club/client/middleware/response/emit.rb +20 -0
- data/lib/internet_scrabble_club/client/middleware/response/parse.rb +26 -0
- data/lib/internet_scrabble_club/client/middleware/response/read.rb +21 -0
- data/lib/internet_scrabble_club/client/middleware/response/transform.rb +22 -0
- data/lib/internet_scrabble_club/client/response_parsers.rb +17 -0
- data/lib/internet_scrabble_club/client/response_parsers/base.rb +78 -0
- data/lib/internet_scrabble_club/client/response_parsers/close.rb +14 -0
- data/lib/internet_scrabble_club/client/response_parsers/examine.rb +15 -0
- data/lib/internet_scrabble_club/client/response_parsers/examine/history.rb +51 -0
- data/lib/internet_scrabble_club/client/response_parsers/history.rb +17 -0
- data/lib/internet_scrabble_club/client/response_parsers/login.rb +14 -0
- data/lib/internet_scrabble_club/client/response_parsers/ping.rb +13 -0
- data/lib/internet_scrabble_club/client/response_parsers/ping/reply.rb +12 -0
- data/lib/internet_scrabble_club/client/response_parsers/seek.rb +15 -0
- data/lib/internet_scrabble_club/client/response_parsers/unseek.rb +14 -0
- data/lib/internet_scrabble_club/client/response_parsers/who.rb +13 -0
- data/lib/internet_scrabble_club/client/response_parsers/who/list.rb +16 -0
- data/lib/internet_scrabble_club/client/response_parsers/who/move.rb +14 -0
- data/lib/internet_scrabble_club/client/response_transformers.rb +2 -0
- data/lib/internet_scrabble_club/client/response_transformers/base.rb +50 -0
- data/lib/internet_scrabble_club/client/response_transformers/examine/history.rb +22 -0
- metadata +31 -32
- data/lib/internet_scrabble_club/client/extensions/authentication.rb +0 -21
- data/lib/internet_scrabble_club/client/extensions/echo_ping.rb +0 -17
- data/lib/internet_scrabble_club/client/extensions/keep_alive.rb +0 -16
- data/lib/internet_scrabble_club/client/middleware/emit.rb +0 -18
- data/lib/internet_scrabble_club/client/middleware/parse.rb +0 -24
- data/lib/internet_scrabble_club/client/middleware/read.rb +0 -19
- data/lib/internet_scrabble_club/client/middleware/transform.rb +0 -20
- data/lib/internet_scrabble_club/message_parsers.rb +0 -17
- data/lib/internet_scrabble_club/message_parsers/base.rb +0 -76
- data/lib/internet_scrabble_club/message_parsers/close.rb +0 -12
- data/lib/internet_scrabble_club/message_parsers/examine.rb +0 -13
- data/lib/internet_scrabble_club/message_parsers/examine/history.rb +0 -49
- data/lib/internet_scrabble_club/message_parsers/history.rb +0 -15
- data/lib/internet_scrabble_club/message_parsers/login.rb +0 -12
- data/lib/internet_scrabble_club/message_parsers/ping.rb +0 -11
- data/lib/internet_scrabble_club/message_parsers/ping/reply.rb +0 -11
- data/lib/internet_scrabble_club/message_parsers/seek.rb +0 -13
- data/lib/internet_scrabble_club/message_parsers/unseek.rb +0 -12
- data/lib/internet_scrabble_club/message_parsers/who.rb +0 -11
- data/lib/internet_scrabble_club/message_parsers/who/list.rb +0 -14
- data/lib/internet_scrabble_club/message_parsers/who/move.rb +0 -12
- data/lib/internet_scrabble_club/message_transformers.rb +0 -3
- data/lib/internet_scrabble_club/message_transformers/base.rb +0 -48
- data/lib/internet_scrabble_club/message_transformers/examine/history.rb +0 -32
- data/lib/internet_scrabble_club/message_transformers/history.rb +0 -14
- data/lib/internet_scrabble_club/message_transformers/who/move.rb +0 -14
- data/lib/internet_scrabble_club/multi_queue.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfbc285681699ddf19091435e5a89d134670f332
|
4
|
+
data.tar.gz: 249f4e0d5f7681ec4fb2bfa42c7585f411ab09e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f5829c3508db7f815e57fb9630b964d773dca34a51822c90d4db67ca97cdd0bc1a37e3841f681e65647df3f8e773af62fde9cca3797fd2286a199a70d367bd6
|
7
|
+
data.tar.gz: 13ac5b2ef5ff307b5045c73a2ab319b8247a447d0e27a77e5375b057fe66a98c575a2c02164ee24a3efd17902fe6c61719c59d2d90457724393dd522d96f7f1c
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
rbx-2.2.
|
1
|
+
rbx-2.2.10
|
data/Rakefile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/scraper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# TODO: Move to rake task
|
2
|
+
|
3
|
+
require 'internet_scrabble_club/scraper/players'
|
4
|
+
require 'internet_scrabble_club/scraper/games'
|
5
|
+
|
6
|
+
Celluloid.shutdown_timeout = 2 * 60
|
7
|
+
|
8
|
+
InternetScrabbleClub::Scraper::Players.new('iscscraper', 'da39a3ee5e')
|
9
|
+
# InternetScrabbleClub::Scraper::Games.new('iscscraper', 'da39a3ee5e')
|
10
|
+
|
11
|
+
# TODO: Use supervisor group
|
12
|
+
sleep
|
data/bin/spectator.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
require 'celluloid'
|
6
|
+
require 'internet_scrabble_club/client'
|
7
|
+
|
8
|
+
Celluloid.logger.level = Logger::INFO
|
9
|
+
|
10
|
+
class Spectator
|
11
|
+
include Celluloid
|
12
|
+
include InternetScrabbleClub::Entities
|
13
|
+
|
14
|
+
def initialize(nickname, password)
|
15
|
+
@client = InternetScrabbleClub::Client.new
|
16
|
+
@client.authenticate(nickname, password)
|
17
|
+
@client.on_message { |message| handle_message(message) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle_message(message)
|
21
|
+
puts message.inspect
|
22
|
+
# case message.command
|
23
|
+
# when /(UN)?SEEK/
|
24
|
+
# 10.times { |i| @client.send_message('EXAMINE', 'HISTORY', message.nickname, i) }
|
25
|
+
# when /EXAMINE/
|
26
|
+
# game_attributes = construct_game_from_message(message)
|
27
|
+
# persist_game(game_attributes)
|
28
|
+
# end
|
29
|
+
end
|
30
|
+
|
31
|
+
# private
|
32
|
+
|
33
|
+
# def persist_game(game_attributes)
|
34
|
+
# game = Game.create!(game_attributes)
|
35
|
+
# report_game_created(game)
|
36
|
+
# end
|
37
|
+
|
38
|
+
# def report_game_created(game)
|
39
|
+
# puts "(#{game.id}), at #{game.date}, by #{game.players.map(&:nickname).join(' & ')}, #{game.plays.count} plays in total."
|
40
|
+
# end
|
41
|
+
|
42
|
+
# def construct_game_from_message(message)
|
43
|
+
# players = collect_from_setup(message) do |setup|
|
44
|
+
# nickname, rating = setup.values_at(:nickname, :rating)
|
45
|
+
# player = Player.new(nickname: nickname, rating: rating)
|
46
|
+
# end
|
47
|
+
|
48
|
+
# initial_racks = collect_from_setup(message) { |setup| setup.fetch(:initial_rack) }
|
49
|
+
# final_scores = collect_from_setup(message) { |setup| setup.fetch(:final_score) }
|
50
|
+
|
51
|
+
# create_play_object = ->(play_attributes) {
|
52
|
+
# case play_attributes.delete(:type)
|
53
|
+
# when 'MOVE' then Plays::Move.new(play_attributes)
|
54
|
+
# when 'CHANGE' then Plays::Change.new(play_attributes)
|
55
|
+
# when 'PAS' then Plays::Pass.new(play_attributes)
|
56
|
+
# end
|
57
|
+
# }
|
58
|
+
|
59
|
+
# first_player_plays = message.first_player.plays.map do |play|
|
60
|
+
# create_play_object.call(play.to_h.merge(player: players[0]))
|
61
|
+
# end
|
62
|
+
|
63
|
+
# second_player_plays = message.second_player.plays.map do |play|
|
64
|
+
# create_play_object.call(play.to_h.merge(player: players[1]))
|
65
|
+
# end
|
66
|
+
|
67
|
+
# plays = first_player_plays.zip(second_player_plays)
|
68
|
+
# .flatten(1).compact.each_with_index.map { |play, index| play.index = index; play }
|
69
|
+
|
70
|
+
# { date: message.date,
|
71
|
+
# dictionary_code: message.dictionary_code,
|
72
|
+
# initial_racks: initial_racks,
|
73
|
+
# final_scores: final_scores,
|
74
|
+
# plays: plays,
|
75
|
+
# players: players
|
76
|
+
# }
|
77
|
+
# end
|
78
|
+
|
79
|
+
# def collect_from_setup(message)
|
80
|
+
# [:first, :second].collect { |prefix| yield(message.send(:"#{prefix}_player").setup) }
|
81
|
+
# end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
if ARGV.length < 2
|
86
|
+
fail "Please provide nickname and password: bin/spectator <nickname> <password>"
|
87
|
+
end
|
88
|
+
|
89
|
+
Spectator.new(ARGV[0], ARGV[1])
|
90
|
+
|
91
|
+
sleep
|
@@ -1,7 +1,6 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
require 'internet_scrabble_club'
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../lib/internet_scrabble_club', __FILE__)
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
spec.name = 'internet_scrabble_club'
|
@@ -6,72 +6,76 @@ require 'celluloid/autostart'
|
|
6
6
|
require 'events'
|
7
7
|
require 'middleware'
|
8
8
|
|
9
|
-
require_relative '
|
10
|
-
|
11
|
-
require_relative 'client/extensions/authentication'
|
12
|
-
require_relative 'client/extensions/echo_ping'
|
13
|
-
require_relative 'client/extensions/keep_alive'
|
14
|
-
|
15
|
-
require_relative 'client/middleware'
|
9
|
+
require_relative './client/callback_queue'
|
10
|
+
require_relative './client/middleware'
|
16
11
|
|
17
12
|
module InternetScrabbleClub
|
18
13
|
|
19
14
|
class Client
|
20
15
|
include Celluloid::IO
|
21
16
|
|
22
|
-
prepend Extensions::Authentication
|
23
|
-
prepend Extensions::EchoPing
|
24
|
-
prepend Extensions::KeepAlive
|
25
|
-
|
26
17
|
finalizer :finalize
|
27
18
|
|
28
19
|
attr_writer :socket, :middleware
|
29
20
|
attr_writer :command_callback_queue, :event_emitter
|
30
21
|
|
22
|
+
DEFAULT_HOST = '50.97.175.138'
|
23
|
+
DEFAULT_PORT = 1330
|
24
|
+
|
31
25
|
class InvalidCredentials < StandardError
|
32
26
|
def initialize(message = nil)
|
33
27
|
super(message || "Could not authenticate; the provided credentials are invalid.")
|
34
28
|
end
|
35
29
|
end
|
36
30
|
|
37
|
-
def initialize(host =
|
31
|
+
def initialize(host = DEFAULT_HOST, port = DEFAULT_PORT)
|
38
32
|
@socket = TCPSocket.new(host, port)
|
39
|
-
@command_callback_queue =
|
33
|
+
@command_callback_queue = Client::CallbackQueue.new
|
34
|
+
|
40
35
|
@event_emitter = Events::EventEmitter.new
|
41
|
-
@event_emitter.on(:
|
36
|
+
@event_emitter.on(:response) { |response| yield_command_callback(response) }
|
37
|
+
|
38
|
+
Celluloid.every(50) { send_request('SEEK') }
|
42
39
|
|
43
40
|
async.run
|
44
41
|
end
|
45
42
|
|
46
43
|
def run
|
47
|
-
loop {
|
44
|
+
loop { middleware_stack.call({}) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def authenticate(nickname, password, &callback)
|
48
|
+
send_request('LOGIN', nickname, password, 1871, '?', &callback)
|
48
49
|
end
|
49
50
|
|
50
|
-
def
|
51
|
-
@event_emitter.on(:
|
51
|
+
def on_response(command_regex = /.*/, &callback)
|
52
|
+
@event_emitter.on(:response) do |response|
|
53
|
+
callback.call(response) if response[:command] =~ command_regex
|
54
|
+
end
|
52
55
|
end
|
53
56
|
|
54
|
-
def
|
55
|
-
|
56
|
-
@command_callback_queue.enqueue(command
|
57
|
-
@socket.write("\0" <<
|
57
|
+
def send_request(command, *arguments, &callback)
|
58
|
+
request = ['0', command, *arguments].join(' ')
|
59
|
+
@command_callback_queue.enqueue(command, callback)
|
60
|
+
@socket.write("\0" << request.length << request)
|
58
61
|
end
|
59
62
|
|
60
63
|
def finalize
|
61
64
|
@socket.close if @socket
|
62
65
|
end
|
63
66
|
|
64
|
-
private def yield_command_callback(
|
65
|
-
command_callback = @command_callback_queue.dequeue(
|
66
|
-
command_callback.call(
|
67
|
+
private def yield_command_callback(response)
|
68
|
+
command_callback = @command_callback_queue.dequeue(response[:command]) { proc {} }
|
69
|
+
command_callback.call(response)
|
67
70
|
end
|
68
71
|
|
69
|
-
private def
|
70
|
-
@
|
71
|
-
mw.use(Middleware::Read, @socket)
|
72
|
-
mw.use(Middleware::Parse)
|
73
|
-
mw.use(Middleware::Transform)
|
74
|
-
mw.use(Middleware::Emit, @event_emitter)
|
72
|
+
private def middleware_stack
|
73
|
+
@middleware_stack ||= ::Middleware::Builder.new.tap { |mw|
|
74
|
+
mw.use(Middleware::Response::Read, @socket)
|
75
|
+
mw.use(Middleware::Response::Parse)
|
76
|
+
mw.use(Middleware::Response::Transform)
|
77
|
+
mw.use(Middleware::Response::Emit, @event_emitter)
|
78
|
+
mw.use(Middleware::Request::EchoPing, self)
|
75
79
|
}
|
76
80
|
end
|
77
81
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
|
4
|
+
class CallbackQueue
|
5
|
+
def initialize
|
6
|
+
@queues = Hash.new { Array.new }
|
7
|
+
end
|
8
|
+
|
9
|
+
def enqueue(command, callback)
|
10
|
+
@queues[command] += [callback]
|
11
|
+
end
|
12
|
+
|
13
|
+
def dequeue(command, default = nil)
|
14
|
+
return @queues[command].shift if @queues[command].any?
|
15
|
+
return yield(command) if block_given?
|
16
|
+
return default unless default.nil?
|
17
|
+
fail ArgumentError, "Missing callback handler for command #{command}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
|
-
require_relative 'middleware/
|
2
|
-
require_relative 'middleware/
|
3
|
-
|
4
|
-
require_relative 'middleware/
|
1
|
+
require_relative 'middleware/request/echo_ping'
|
2
|
+
require_relative 'middleware/request/keep_alive'
|
3
|
+
|
4
|
+
require_relative 'middleware/response/emit'
|
5
|
+
require_relative 'middleware/response/parse'
|
6
|
+
require_relative 'middleware/response/read'
|
7
|
+
require_relative 'middleware/response/transform'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
module Middleware
|
4
|
+
module Request
|
5
|
+
|
6
|
+
class EchoPing
|
7
|
+
def initialize(stack, client)
|
8
|
+
@stack, @client = stack, client
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
if env.fetch(:response).values_at(:command, :sub_command) == %w(PING REPLY)
|
13
|
+
@client.send_request('PING', 'REPLY')
|
14
|
+
end
|
15
|
+
|
16
|
+
@stack.call(env)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
require 'celluloid/logger'
|
3
|
+
|
4
|
+
module InternetScrabbleClub
|
5
|
+
class Client
|
6
|
+
module Middleware
|
7
|
+
module Request
|
8
|
+
|
9
|
+
class KeepAlive
|
10
|
+
def initialize(stack, client)
|
11
|
+
@stack, @client = stack, client
|
12
|
+
Celluloid.every(50) { keep_alive; log_keep_alive }
|
13
|
+
end
|
14
|
+
|
15
|
+
def keep_alive
|
16
|
+
@client.send_request('SEEK')
|
17
|
+
end
|
18
|
+
|
19
|
+
private def log_keep_alive
|
20
|
+
Celluloid::Logger.info("Sent keep alive command!")
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
@stack.call(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
module Middleware
|
4
|
+
module Response
|
5
|
+
|
6
|
+
class Emit
|
7
|
+
def initialize(stack, event_emitter)
|
8
|
+
@stack, @event_emitter = stack, event_emitter
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@event_emitter.emit(:response, env[:response])
|
13
|
+
@stack.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require 'celluloid/logger'
|
3
|
+
require_relative '../../response_parsers'
|
4
|
+
|
5
|
+
module InternetScrabbleClub
|
6
|
+
class Client
|
7
|
+
module Middleware
|
8
|
+
module Response
|
9
|
+
|
10
|
+
class Parse
|
11
|
+
def initialize(stack, response_parser = ResponseParsers::Base.new)
|
12
|
+
@stack, @response_parser = stack, response_parser
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
env[:response] = @response_parser.parse(env[:response])
|
17
|
+
@stack.call(env)
|
18
|
+
rescue Parslet::ParseFailed
|
19
|
+
Celluloid::Logger.warn("Failed to parse response: #{env[:response]}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
module Middleware
|
4
|
+
module Response
|
5
|
+
|
6
|
+
class Read
|
7
|
+
def initialize(stack, socket)
|
8
|
+
@stack, @socket = stack, socket
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
response_length = @socket.getc.ord * 256 + @socket.getc.ord
|
13
|
+
env[:response] = @socket.read(response_length)
|
14
|
+
@stack.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative '../../response_transformers'
|
2
|
+
|
3
|
+
module InternetScrabbleClub
|
4
|
+
class Client
|
5
|
+
module Middleware
|
6
|
+
module Response
|
7
|
+
|
8
|
+
class Transform
|
9
|
+
def initialize(stack, response_transformer = ResponseTransformers::Base.new)
|
10
|
+
@stack, @response_transformer = stack, response_transformer
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
env[:response] = @response_transformer.apply(env[:response])
|
15
|
+
@stack.call(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|