internet_scrabble_club 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +26 -0
- data/Rakefile +1 -0
- data/internet_scrabble_club.gemspec +29 -0
- data/lib/internet_scrabble_club/client/extensions/authentication.rb +21 -0
- data/lib/internet_scrabble_club/client/extensions/echo_ping.rb +17 -0
- data/lib/internet_scrabble_club/client/extensions/keep_alive.rb +16 -0
- data/lib/internet_scrabble_club/client/middleware/emit.rb +18 -0
- data/lib/internet_scrabble_club/client/middleware/parse.rb +24 -0
- data/lib/internet_scrabble_club/client/middleware/read.rb +19 -0
- data/lib/internet_scrabble_club/client/middleware/transform.rb +20 -0
- data/lib/internet_scrabble_club/client/middleware.rb +4 -0
- data/lib/internet_scrabble_club/client.rb +79 -0
- data/lib/internet_scrabble_club/message_parsers/base.rb +76 -0
- data/lib/internet_scrabble_club/message_parsers/close.rb +12 -0
- data/lib/internet_scrabble_club/message_parsers/examine/history.rb +49 -0
- data/lib/internet_scrabble_club/message_parsers/examine.rb +13 -0
- data/lib/internet_scrabble_club/message_parsers/history.rb +15 -0
- data/lib/internet_scrabble_club/message_parsers/login.rb +12 -0
- data/lib/internet_scrabble_club/message_parsers/ping/reply.rb +11 -0
- data/lib/internet_scrabble_club/message_parsers/ping.rb +11 -0
- data/lib/internet_scrabble_club/message_parsers/seek.rb +13 -0
- data/lib/internet_scrabble_club/message_parsers/unseek.rb +12 -0
- data/lib/internet_scrabble_club/message_parsers/who/list.rb +14 -0
- data/lib/internet_scrabble_club/message_parsers/who/move.rb +12 -0
- data/lib/internet_scrabble_club/message_parsers/who.rb +11 -0
- data/lib/internet_scrabble_club/message_parsers.rb +17 -0
- data/lib/internet_scrabble_club/message_transformers/base.rb +48 -0
- data/lib/internet_scrabble_club/message_transformers/examine/history.rb +32 -0
- data/lib/internet_scrabble_club/message_transformers/history.rb +14 -0
- data/lib/internet_scrabble_club/message_transformers/who/move.rb +14 -0
- data/lib/internet_scrabble_club/message_transformers.rb +3 -0
- data/lib/internet_scrabble_club/multi_queue.rb +20 -0
- data/lib/internet_scrabble_club.rb +3 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8a765c05c9d571f3d34382874e9d6a685a66f5cd
|
4
|
+
data.tar.gz: 86ec3896b32848b07ca04fe029d938aa36a22ba1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 600ea4f6ac6284f56e9fa316547ecc3ee046f292de920ebef4222b92e1ea2792757e9b52a5ec02808a10bca27e49a9af564b87a4fea58bba74494e8001e2bbe1
|
7
|
+
data.tar.gz: 049623316e1dc70b325df7c9a951eed3d76900985f5d683944eb644374f47c79f464876f302a422fe97d9dd63d309b801a29ea16feccb952bd5a156fdf77b528
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rbx-2.2.6
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Thomas Brus
|
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,26 @@
|
|
1
|
+
# ISC Ruby client
|
2
|
+
|
3
|
+
Ruby client for interacting with the [Internet Scrabble Club](http://www.isc.ro) server.
|
4
|
+
|
5
|
+
## Example
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
require 'internet_scrabble_club'
|
9
|
+
|
10
|
+
client = InternetScrabbleClub::Client.new
|
11
|
+
|
12
|
+
client.authenticate('nickname', 'password') do |login|
|
13
|
+
# Authenticated ...
|
14
|
+
puts login.greeting # => "Welcome to the Internet Scrabble Club, <nickname>"
|
15
|
+
|
16
|
+
# Fetch history (last ten games)
|
17
|
+
client.send_message('HISTORY', 'thomas92') do |history|
|
18
|
+
puts history.entries.first # => #<OpenStruct index=1, first_player="thomas92" ... >
|
19
|
+
end
|
20
|
+
|
21
|
+
# Inspect a recent game
|
22
|
+
client.send_message('EXAMINE', 'HISTORY', 'thomas92', 0) do |game|
|
23
|
+
puts game.inspect # => #<OpenStruct date=#<Date ... >, players=["thomas92", ...] ... >
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'internet_scrabble_club'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "internet_scrabble_club"
|
8
|
+
spec.version = InternetScrabbleClub::VERSION
|
9
|
+
spec.authors = ["Thomas Brus"]
|
10
|
+
spec.email = ["thomas.brus@me.com"]
|
11
|
+
spec.summary = %q{Fetches scrabble games from the Internet Scrabble Club.}
|
12
|
+
spec.homepage = "https://github.com/thomasbrus/internet-scrabble-club"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
|
23
|
+
spec.add_dependency "celluloid-io", "~> 0.15.0"
|
24
|
+
spec.add_dependency "parslet", "~> 1.5.0"
|
25
|
+
spec.add_dependency "descendants_tracker", "~> 0.0.4"
|
26
|
+
spec.add_dependency "events", "~> 0.9.8"
|
27
|
+
spec.add_dependency "anima", "~> 0.2.0"
|
28
|
+
spec.add_dependency "middleware", "~> 0.1.0"
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
module Extensions
|
4
|
+
|
5
|
+
module Authentication
|
6
|
+
def initialize(*args, &block)
|
7
|
+
super; @event_emitter.on(:message) do |message|
|
8
|
+
if message.command == 'CLOSE' && message.reason =~ /^Invalid password/
|
9
|
+
fail InvalidCredentials
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def authenticate(nickname, password, &callback)
|
15
|
+
send_message('LOGIN', nickname, password, 1871, '?', &callback)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
module Extensions
|
4
|
+
|
5
|
+
module EchoPing
|
6
|
+
def initialize(*args, &block)
|
7
|
+
super; @event_emitter.on(:message) do |message|
|
8
|
+
if [message.command, message.sub_command] == %w(PING REPLY)
|
9
|
+
send_message('PING', 'REPLY')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
module Middleware
|
4
|
+
|
5
|
+
class Emit
|
6
|
+
def initialize(stack, event_emitter)
|
7
|
+
@stack, @event_emitter = stack, event_emitter
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@event_emitter.emit(:message, env[:message])
|
12
|
+
@stack.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require 'celluloid/logger'
|
3
|
+
require_relative '../../message_parsers'
|
4
|
+
|
5
|
+
module InternetScrabbleClub
|
6
|
+
class Client
|
7
|
+
module Middleware
|
8
|
+
|
9
|
+
class Parse
|
10
|
+
def initialize(stack, message_parser = MessageParsers::Base.new)
|
11
|
+
@stack, @message_parser = stack, message_parser
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
env[:message] = @message_parser.parse(env[:message])
|
16
|
+
@stack.call(env)
|
17
|
+
rescue Parslet::ParseFailed
|
18
|
+
Celluloid::Logger.warn("Failed to parse message: #{env[:message]}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
class Client
|
3
|
+
module Middleware
|
4
|
+
|
5
|
+
class Read
|
6
|
+
def initialize(stack, socket)
|
7
|
+
@stack, @socket = stack, socket
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
message_length = @socket.getc.ord * 256 + @socket.getc.ord
|
12
|
+
env[:message] = @socket.read(message_length)
|
13
|
+
@stack.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative '../../message_transformers'
|
2
|
+
|
3
|
+
module InternetScrabbleClub
|
4
|
+
class Client
|
5
|
+
module Middleware
|
6
|
+
|
7
|
+
class Transform
|
8
|
+
def initialize(stack, message_transformer = MessageTransformers::Base.new)
|
9
|
+
@stack, @message_transformer = stack, message_transformer
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
env[:message] = @message_transformer.apply(env[:message])
|
14
|
+
@stack.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
require 'celluloid/io'
|
4
|
+
require 'celluloid/autostart'
|
5
|
+
|
6
|
+
require 'events'
|
7
|
+
require 'middleware'
|
8
|
+
|
9
|
+
require_relative 'multi_queue'
|
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'
|
16
|
+
|
17
|
+
module InternetScrabbleClub
|
18
|
+
|
19
|
+
class Client
|
20
|
+
include Celluloid::IO
|
21
|
+
|
22
|
+
prepend Extensions::Authentication
|
23
|
+
prepend Extensions::EchoPing
|
24
|
+
prepend Extensions::KeepAlive
|
25
|
+
|
26
|
+
finalizer :finalize
|
27
|
+
|
28
|
+
attr_writer :socket, :middleware
|
29
|
+
attr_writer :command_callback_queue, :event_emitter
|
30
|
+
|
31
|
+
class InvalidCredentials < StandardError
|
32
|
+
def initialize(message = nil)
|
33
|
+
super(message || "Could not authenticate; the provided credentials are invalid.")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(host = '50.97.175.138', port = 1330)
|
38
|
+
@socket = TCPSocket.new(host, port)
|
39
|
+
@command_callback_queue = MultiQueue.new
|
40
|
+
@event_emitter = Events::EventEmitter.new
|
41
|
+
@event_emitter.on(:message) { |message| yield_command_callback(message) }
|
42
|
+
|
43
|
+
async.run
|
44
|
+
end
|
45
|
+
|
46
|
+
def run
|
47
|
+
loop { middleware.call({}) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_message(&callback)
|
51
|
+
@event_emitter.on(:message, &callback)
|
52
|
+
end
|
53
|
+
|
54
|
+
def send_message(command, *arguments, &callback)
|
55
|
+
message = ['0', command, *arguments].join(' ')
|
56
|
+
@command_callback_queue.enqueue(command.downcase.to_sym, callback)
|
57
|
+
@socket.write("\0" << message.length << message)
|
58
|
+
end
|
59
|
+
|
60
|
+
def finalize
|
61
|
+
@socket.close if @socket
|
62
|
+
end
|
63
|
+
|
64
|
+
private def yield_command_callback(message)
|
65
|
+
command_callback = @command_callback_queue.dequeue(message.command) { proc {} }
|
66
|
+
command_callback.call(message)
|
67
|
+
end
|
68
|
+
|
69
|
+
private def middleware
|
70
|
+
@middleware ||= ::Middleware::Builder.new.tap { |mw|
|
71
|
+
mw.use(Middleware::Read, @socket)
|
72
|
+
mw.use(Middleware::Parse)
|
73
|
+
mw.use(Middleware::Transform)
|
74
|
+
mw.use(Middleware::Emit, @event_emitter)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require 'descendants_tracker'
|
3
|
+
|
4
|
+
module InternetScrabbleClub
|
5
|
+
module MessageParsers
|
6
|
+
|
7
|
+
class Base < Parslet::Parser
|
8
|
+
extend DescendantsTracker
|
9
|
+
|
10
|
+
root(:message)
|
11
|
+
|
12
|
+
rule(:message) { descendant_parsers.reduce(:|) or (digit >> space >>
|
13
|
+
( command_with_sub_command_and_arguments |
|
14
|
+
command_with_arguments | command_with_sub_command)) }
|
15
|
+
|
16
|
+
rule(:command_with_arguments) { command.as(:command) >> space >>
|
17
|
+
arguments.as(:arguments) }
|
18
|
+
|
19
|
+
rule(:command_with_sub_command_and_arguments) { command_with_sub_command >>
|
20
|
+
space >> arguments.as(:arguments) }
|
21
|
+
|
22
|
+
rule(:command_with_sub_command) { command.as(:command) >>
|
23
|
+
space >> sub_command.as(:sub_command) }
|
24
|
+
|
25
|
+
rule(:command) { nothing }
|
26
|
+
rule(:sub_command) { nothing }
|
27
|
+
rule(:arguments) { nothing }
|
28
|
+
|
29
|
+
rule(:nothing) { str('') }
|
30
|
+
|
31
|
+
rule(:space) { str(' ') }
|
32
|
+
rule(:space?) { space.maybe }
|
33
|
+
|
34
|
+
rule(:newline) { match('\n') }
|
35
|
+
rule(:newline?) { newline.maybe }
|
36
|
+
|
37
|
+
rule(:colon) { str(':') }
|
38
|
+
rule(:semicolon) { str(';') }
|
39
|
+
rule(:minus) { str('-') }
|
40
|
+
|
41
|
+
rule(:digit) { match['0-9'] }
|
42
|
+
rule(:alpha) { match['A-Za-z'] }
|
43
|
+
|
44
|
+
rule(:int) { _int.as(:int) }
|
45
|
+
rule(:_int) { minus.maybe >> digit.repeat(1) }
|
46
|
+
|
47
|
+
rule(:dashes) { str('---').as(:dashes) }
|
48
|
+
rule(:null) { str('null').as(:null) }
|
49
|
+
|
50
|
+
rule(:word) { (digit | alpha).repeat(1).as(:word) }
|
51
|
+
rule(:sentence) { (match['^.'].repeat(1) >> str('.')).as(:sentence) }
|
52
|
+
rule(:text) { any.repeat(1).as(:text) }
|
53
|
+
|
54
|
+
rule(:tiles) { (alpha | str('?')).repeat(1).as(:tiles) }
|
55
|
+
rule(:rack) { dashes | tiles }
|
56
|
+
|
57
|
+
rule(:month_day) { digit.repeat(1, 2) }
|
58
|
+
rule(:short_month) { match['A-Z'] >> match['a-z'].repeat(2, 2) }
|
59
|
+
rule(:short_year) { digit >> digit }
|
60
|
+
rule(:date) { (delimited [month_day, short_month, short_year], colon).as(:date) }
|
61
|
+
|
62
|
+
rule(:dictionary) { str('TWL') | str('SOWPODS') | str('ODS') | str('LOC') |
|
63
|
+
str('SWL') | str('PARO') | str('MULTI') }
|
64
|
+
|
65
|
+
def delimited(sequence, seperator = space)
|
66
|
+
seperators = Array.new(sequence.length - 1) { seperator }
|
67
|
+
sequence.zip(seperators).flatten(1).compact.inject(:>>)
|
68
|
+
end
|
69
|
+
|
70
|
+
private def descendant_parsers
|
71
|
+
@descendant_parsers ||= self.class.descendants.map(&:new)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative '../examine'
|
2
|
+
|
3
|
+
module InternetScrabbleClub
|
4
|
+
module MessageParsers
|
5
|
+
|
6
|
+
class Examine::History < Examine
|
7
|
+
rule(:sub_command) { str('HISTORY') }
|
8
|
+
|
9
|
+
rule(:arguments) { delimited([stats, settings,
|
10
|
+
(player_setup.as(:setup) >> newline_with_whitespace >> plays.as(:plays)
|
11
|
+
).as(:first_player), str('STOP'),
|
12
|
+
(player_setup.as(:setup) >> newline_with_whitespace >> plays.as(:plays)
|
13
|
+
).as(:second_player), str('STOP')
|
14
|
+
], newline_with_whitespace) >> newline_with_whitespace }
|
15
|
+
|
16
|
+
rule(:newline_with_whitespace) { space.repeat >> newline >> space.repeat }
|
17
|
+
|
18
|
+
rule(:stats) { _int >> space >> date.as(:date) }
|
19
|
+
rule(:settings) { delimited [int.dictionary_code, _int, _int, _int] }
|
20
|
+
|
21
|
+
rule(:player_setup) { delimited [word.as(:nickname), int.as(:rating),
|
22
|
+
rack.as(:initial_rack), (int | null).as(:final_score)] }
|
23
|
+
|
24
|
+
rule(:plays) { ((move | change | pass) >> space?).repeat }
|
25
|
+
|
26
|
+
rule(:move) { delimited [str('MOVE').as(:word).as(:type), position.as(:position),
|
27
|
+
tiles.as(:word), int.as(:score), _int, _int, rack.as(:rack), _int] }
|
28
|
+
|
29
|
+
rule(:change) { delimited [str('CHANGE').as(:word).as(:type), rack.as(:rack),
|
30
|
+
_int, _int, (dashes | int).as(:swap_count)] }
|
31
|
+
|
32
|
+
rule(:pass) { delimited [str('PAS').as(:word).as(:type), _int, _int, (dashes |
|
33
|
+
suggestion).as(:suggestion)] }
|
34
|
+
|
35
|
+
rule(:suggestion) { delimited [position.as(:position), word.as(:word),
|
36
|
+
int.as(:score)], underscore }
|
37
|
+
|
38
|
+
rule(:underscore) { match('_') }
|
39
|
+
rule(:underscored_word) { word >> underscore >> word }
|
40
|
+
|
41
|
+
rule(:position) { horizontal_position.as(:horizontal) |
|
42
|
+
vertical_position.as(:vertical) }
|
43
|
+
|
44
|
+
rule(:horizontal_position) { alpha.as(:word).as(:column) >> int.as(:row) }
|
45
|
+
rule(:vertical_position) { int.as(:row) >> alpha.as(:word).as(:column) }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module InternetScrabbleClub
|
4
|
+
module MessageParsers
|
5
|
+
|
6
|
+
class Examine < Base
|
7
|
+
rule(:command) { str('EXAMINE') }
|
8
|
+
rule(:command_with_sub_command_and_arguments) { command_with_sub_command >>
|
9
|
+
space? >> newline >> arguments.as(:arguments) }
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module InternetScrabbleClub
|
4
|
+
module MessageParsers
|
5
|
+
|
6
|
+
class History < Base
|
7
|
+
rule(:command) { str('HISTORY') }
|
8
|
+
rule(:arguments) { word.as(:subject) >> space >> entries.as(:entries) }
|
9
|
+
rule(:entries) { (entry.as(:entry) >> space?).repeat(1) }
|
10
|
+
rule(:entry) { delimited [digit.as(:index), players, dictionary, _int, date.as(:date)] }
|
11
|
+
rule(:players) { delimited [_int, word.as(:first_player), _int, word.as(:second_player)] }
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module InternetScrabbleClub
|
4
|
+
module MessageParsers
|
5
|
+
|
6
|
+
class Seek < Base
|
7
|
+
rule(:command) { str('SEEK') }
|
8
|
+
rule(:arguments) { delimited [int.as(:rating), word.as(:nickname), settings] }
|
9
|
+
rule(:settings) { delimited([_int] * 7) }
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative '../who'
|
2
|
+
|
3
|
+
module InternetScrabbleClub
|
4
|
+
module MessageParsers
|
5
|
+
|
6
|
+
class Who::List < Who
|
7
|
+
rule(:sub_command) { str('LIST') }
|
8
|
+
rule(:arguments) { int.as(:count) >> space >> space >> users.as(:users) }
|
9
|
+
rule(:users) { (user.as(:user) >> space?).repeat(1) }
|
10
|
+
rule(:user) { delimited [int.as(:rating), word.as(:nickname), any.as(:status), digit, digit] }
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'message_parsers/close'
|
2
|
+
|
3
|
+
require_relative 'message_parsers/examine'
|
4
|
+
require_relative 'message_parsers/examine/history'
|
5
|
+
|
6
|
+
require_relative 'message_parsers/who'
|
7
|
+
require_relative 'message_parsers/who/list'
|
8
|
+
require_relative 'message_parsers/who/move'
|
9
|
+
|
10
|
+
require_relative 'message_parsers/history'
|
11
|
+
require_relative 'message_parsers/login'
|
12
|
+
|
13
|
+
require_relative 'message_parsers/ping'
|
14
|
+
require_relative 'message_parsers/ping/reply'
|
15
|
+
|
16
|
+
require_relative 'message_parsers/seek'
|
17
|
+
require_relative 'message_parsers/unseek'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'parslet'
|
3
|
+
require 'descendants_tracker'
|
4
|
+
|
5
|
+
module InternetScrabbleClub
|
6
|
+
class MessageTransformers
|
7
|
+
|
8
|
+
class Base < Parslet::Transform
|
9
|
+
extend DescendantsTracker
|
10
|
+
|
11
|
+
rule(command: simple(:cmd), arguments: subtree(:args)) do
|
12
|
+
OpenStruct.new(args.to_h.merge(command: cmd.to_s))
|
13
|
+
end
|
14
|
+
|
15
|
+
rule(command: simple(:cmd), sub_command: simple(:sub_cmd)) do
|
16
|
+
OpenStruct.new(command: cmd.to_s, sub_command: sub_cmd.to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
rule(command: simple(:cmd), sub_command: simple(:sub_cmd), arguments: subtree(:args)) do
|
20
|
+
OpenStruct.new(args.to_h.merge(command: cmd.to_s, sub_command: sub_cmd.to_s))
|
21
|
+
end
|
22
|
+
|
23
|
+
rule(int: simple(:int)) { Integer(int) }
|
24
|
+
|
25
|
+
rule(word: simple(:word)) { word.to_s }
|
26
|
+
rule(sentence: simple(:sentence)) { sentence.to_s }
|
27
|
+
rule(text: simple(:text)) { text.to_s }
|
28
|
+
|
29
|
+
rule(date: simple(:date)) { Date.parse(date.to_s) }
|
30
|
+
|
31
|
+
rule(null: simple(:null)) { 0 }
|
32
|
+
rule(tiles: simple(:tiles)) { tiles.to_s }
|
33
|
+
|
34
|
+
rule(dashes: simple(:dashes)) { nil }
|
35
|
+
|
36
|
+
def apply(tree, context = nil)
|
37
|
+
descendent_transformers.reduce(super) do |tree, transformer|
|
38
|
+
transformer.apply(tree, context)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def descendent_transformers
|
43
|
+
@descendent_transformers ||= self.class.descendants.map(&:new)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require_relative '../base'
|
3
|
+
|
4
|
+
module InternetScrabbleClub
|
5
|
+
class MessageTransformers
|
6
|
+
module Examine
|
7
|
+
|
8
|
+
class History < Base
|
9
|
+
rule(date: simple(:date), dictionary_code: simple(:dictionary_code),
|
10
|
+
first_player: subtree(:first_player), second_player: subtree(:second_player)
|
11
|
+
) do
|
12
|
+
{ date: date, dictionary_code: dictionary_code,
|
13
|
+
first_player: first_player, second_player: second_player
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
rule(setup: subtree(:setup), plays: subtree(:plays)) do
|
18
|
+
OpenStruct.new(setup: setup, plays: plays.map { |play| OpenStruct.new(play) })
|
19
|
+
end
|
20
|
+
|
21
|
+
rule(horizontal: { column: simple(:column), row: simple(:row) }) do
|
22
|
+
{direction: :horizontal, column: column, row: row}
|
23
|
+
end
|
24
|
+
|
25
|
+
rule(vertical: { column: simple(:column), row: simple(:row) }) do
|
26
|
+
{direction: :vertical, column: column, row: row}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module InternetScrabbleClub
|
2
|
+
|
3
|
+
class MultiQueue
|
4
|
+
def initialize
|
5
|
+
@queues = Hash.new { Array.new }
|
6
|
+
end
|
7
|
+
|
8
|
+
def enqueue(name, item)
|
9
|
+
@queues[name] += [item]
|
10
|
+
end
|
11
|
+
|
12
|
+
def dequeue(name, default = nil)
|
13
|
+
return @queues[name].shift if @queues[name].any?
|
14
|
+
return yield(name) if block_given?
|
15
|
+
return default unless default.nil?
|
16
|
+
fail ArgumentError, "Cannot dequeue item from empty queue"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: internet_scrabble_club
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas Brus
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2014-08-29 00:00:00 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
prerelease: false
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: "1.5"
|
22
|
+
type: :development
|
23
|
+
version_requirements: *id001
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: rake
|
26
|
+
prerelease: false
|
27
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- &id009
|
30
|
+
- ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id002
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: celluloid-io
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.15.0
|
43
|
+
type: :runtime
|
44
|
+
version_requirements: *id003
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: parslet
|
47
|
+
prerelease: false
|
48
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ~>
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.5.0
|
53
|
+
type: :runtime
|
54
|
+
version_requirements: *id004
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: descendants_tracker
|
57
|
+
prerelease: false
|
58
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 0.0.4
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id005
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: events
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ~>
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 0.9.8
|
73
|
+
type: :runtime
|
74
|
+
version_requirements: *id006
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: anima
|
77
|
+
prerelease: false
|
78
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.2.0
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id007
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: middleware
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 0.1.0
|
93
|
+
type: :runtime
|
94
|
+
version_requirements: *id008
|
95
|
+
description:
|
96
|
+
email:
|
97
|
+
- thomas.brus@me.com
|
98
|
+
executables: []
|
99
|
+
|
100
|
+
extensions: []
|
101
|
+
|
102
|
+
extra_rdoc_files: []
|
103
|
+
|
104
|
+
files:
|
105
|
+
- .gitignore
|
106
|
+
- .ruby-version
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- internet_scrabble_club.gemspec
|
112
|
+
- lib/internet_scrabble_club.rb
|
113
|
+
- lib/internet_scrabble_club/client.rb
|
114
|
+
- lib/internet_scrabble_club/client/extensions/authentication.rb
|
115
|
+
- lib/internet_scrabble_club/client/extensions/echo_ping.rb
|
116
|
+
- lib/internet_scrabble_club/client/extensions/keep_alive.rb
|
117
|
+
- lib/internet_scrabble_club/client/middleware.rb
|
118
|
+
- lib/internet_scrabble_club/client/middleware/emit.rb
|
119
|
+
- lib/internet_scrabble_club/client/middleware/parse.rb
|
120
|
+
- lib/internet_scrabble_club/client/middleware/read.rb
|
121
|
+
- lib/internet_scrabble_club/client/middleware/transform.rb
|
122
|
+
- lib/internet_scrabble_club/message_parsers.rb
|
123
|
+
- lib/internet_scrabble_club/message_parsers/base.rb
|
124
|
+
- lib/internet_scrabble_club/message_parsers/close.rb
|
125
|
+
- lib/internet_scrabble_club/message_parsers/examine.rb
|
126
|
+
- lib/internet_scrabble_club/message_parsers/examine/history.rb
|
127
|
+
- lib/internet_scrabble_club/message_parsers/history.rb
|
128
|
+
- lib/internet_scrabble_club/message_parsers/login.rb
|
129
|
+
- lib/internet_scrabble_club/message_parsers/ping.rb
|
130
|
+
- lib/internet_scrabble_club/message_parsers/ping/reply.rb
|
131
|
+
- lib/internet_scrabble_club/message_parsers/seek.rb
|
132
|
+
- lib/internet_scrabble_club/message_parsers/unseek.rb
|
133
|
+
- lib/internet_scrabble_club/message_parsers/who.rb
|
134
|
+
- lib/internet_scrabble_club/message_parsers/who/list.rb
|
135
|
+
- lib/internet_scrabble_club/message_parsers/who/move.rb
|
136
|
+
- lib/internet_scrabble_club/message_transformers.rb
|
137
|
+
- lib/internet_scrabble_club/message_transformers/base.rb
|
138
|
+
- lib/internet_scrabble_club/message_transformers/examine/history.rb
|
139
|
+
- lib/internet_scrabble_club/message_transformers/history.rb
|
140
|
+
- lib/internet_scrabble_club/message_transformers/who/move.rb
|
141
|
+
- lib/internet_scrabble_club/multi_queue.rb
|
142
|
+
homepage: https://github.com/thomasbrus/internet-scrabble-club
|
143
|
+
licenses:
|
144
|
+
- MIT
|
145
|
+
metadata: {}
|
146
|
+
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- *id009
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- *id009
|
158
|
+
requirements: []
|
159
|
+
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 2.2.2
|
162
|
+
signing_key:
|
163
|
+
specification_version: 4
|
164
|
+
summary: Fetches scrabble games from the Internet Scrabble Club.
|
165
|
+
test_files: []
|
166
|
+
|
167
|
+
has_rdoc:
|