weskit 0.0.2
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.
- data/.gitignore +5 -0
- data/Gemfile +2 -0
- data/README +2 -0
- data/Rakefile +1 -0
- data/lib/weskit.rb +3 -0
- data/lib/weskit/lobby.rb +11 -0
- data/lib/weskit/lobby/bot.rb +85 -0
- data/lib/weskit/lobby/bot_error.rb +3 -0
- data/lib/weskit/version.rb +3 -0
- data/lib/weskit/wml.rb +11 -0
- data/lib/weskit/wml/node.rb +76 -0
- data/lib/weskit/wml/node_array.rb +25 -0
- data/lib/weskit/wml/node_error.rb +3 -0
- data/lib/weskit/wml/reader.rb +67 -0
- data/lib/weskit/wml/reader_error.rb +3 -0
- data/lib/weskit/wml/string_extensions.rb +59 -0
- data/weskit.gemspec +24 -0
- metadata +82 -0
data/Gemfile
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/weskit.rb
ADDED
data/lib/weskit/lobby.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'weskit/wml'
|
2
|
+
|
3
|
+
module Weskit::Lobby
|
4
|
+
require 'socket'
|
5
|
+
require 'stringio'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
class Bot
|
9
|
+
def initialize nickname, options = {}
|
10
|
+
defaults = {
|
11
|
+
:debug => false,
|
12
|
+
:port => 15000,
|
13
|
+
:server => 'server.wesnoth.org',
|
14
|
+
:version => '1.10.0'
|
15
|
+
}
|
16
|
+
options = defaults.merge options
|
17
|
+
|
18
|
+
@server = options[:server]
|
19
|
+
@port = options[:port]
|
20
|
+
@version = options[:version]
|
21
|
+
|
22
|
+
@reader = Weskit::WML::Reader.new
|
23
|
+
@nickname = nickname
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect_and &operations
|
27
|
+
@socket = TCPSocket.new @server, @port
|
28
|
+
basic_communication
|
29
|
+
response = read_wml
|
30
|
+
@socket.close
|
31
|
+
|
32
|
+
@socket = TCPSocket.new response.redirect[:host], response.redirect[:port]
|
33
|
+
basic_communication
|
34
|
+
|
35
|
+
raise "Server send node other than 'mustlogin'" if read_wml.mustlogin.nil?
|
36
|
+
send_wml 'login', :selective_ping => 1, :username => @nickname
|
37
|
+
raise "Server send node other than 'join_lobby'" if read_wml.join_lobby.nil?
|
38
|
+
|
39
|
+
result = yield self
|
40
|
+
|
41
|
+
sleep 3
|
42
|
+
@socket.close
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
def read_wml
|
47
|
+
wml = @reader.read_str read_str
|
48
|
+
puts "\e[0;33mRecieve MWL\e[0m:\n#{wml}" if @debug
|
49
|
+
wml
|
50
|
+
end
|
51
|
+
|
52
|
+
def send_wml node, contents = {}
|
53
|
+
wml = @reader.read_hsh({node => contents}).to_s
|
54
|
+
puts "\e[0;32mSend MWL\e[0m:\n#{wml}" if @debug
|
55
|
+
send_str wml
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def basic_communication
|
61
|
+
handshake
|
62
|
+
raise "Server send node other than 'version'" if read_wml.version.empty?
|
63
|
+
send_wml 'version', :version => @version
|
64
|
+
end
|
65
|
+
|
66
|
+
def handshake
|
67
|
+
@socket.send "\x00" * 4, 0
|
68
|
+
@socket.read 4
|
69
|
+
end
|
70
|
+
|
71
|
+
def read_str
|
72
|
+
msg_size = @socket.read(4).unpack('N').first
|
73
|
+
msg_data = @socket.read(msg_size)
|
74
|
+
Zlib::GzipReader.new(StringIO.new(msg_data)).read
|
75
|
+
end
|
76
|
+
|
77
|
+
def send_str msg
|
78
|
+
gzw = Zlib::GzipWriter.new StringIO.new
|
79
|
+
gzw.write msg.to_s
|
80
|
+
gzm = gzw.close
|
81
|
+
size = [gzm.size].pack('N')
|
82
|
+
@socket.send(size + gzm.string, 0)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/weskit/wml.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
class Node
|
3
|
+
FALSE_ATTR_VALS = ["no", "false", "off", "uninitialized"]
|
4
|
+
TRUE_ATTR_VALS = ["yes", "true", "on"]
|
5
|
+
|
6
|
+
attr_reader :name, :parent
|
7
|
+
|
8
|
+
def add element
|
9
|
+
return false unless element.kind_of? Node
|
10
|
+
@nodes << element ; true
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create name = '', parent = nil
|
14
|
+
new name, parent
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize name = '', parent = nil
|
18
|
+
name = normalize name
|
19
|
+
name = (name.empty?) ? 'root' : name
|
20
|
+
raise(NodeError, "Node name can consist only of '#{StringExtensions::VALID_CHARS}'") unless name =~ /^#{StringExtensions::VALID_CHARS}+$/
|
21
|
+
raise(NodeError, "Attempt to set inproper parent node") unless parent.kind_of? Node or parent == nil
|
22
|
+
@attributes, @name, @nodes, @parent = {}, name, [], parent
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing method
|
26
|
+
nodes = @nodes.select {|node| node.name == normalize(method)}
|
27
|
+
NodeArray.new.replace nodes
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_ary
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s indent_width = 1
|
34
|
+
indent_width -= 1 if @name == 'root'
|
35
|
+
|
36
|
+
base_indentation = " " * 4
|
37
|
+
contents_indentation = base_indentation * indent_width
|
38
|
+
closing_tag_indentation = base_indentation * (indent_width > 0 ? indent_width - 1 : 0)
|
39
|
+
|
40
|
+
attr_str = @attributes.keys.sort.inject('') {|string, key| string << "#{contents_indentation}#{key}=\"#{@attributes[key]}\"\n"}
|
41
|
+
node_str = @nodes.inject('') {|string, node| string << "#{contents_indentation}#{node.to_s(indent_width + 1)}\n"}
|
42
|
+
|
43
|
+
(@name == 'root') ? "#{attr_str}#{node_str}" : "[#{name}]\n#{attr_str}#{node_str}#{closing_tag_indentation}[/#{name}]"
|
44
|
+
end
|
45
|
+
|
46
|
+
def [] key
|
47
|
+
value = @attributes[normalize key]
|
48
|
+
|
49
|
+
if FALSE_ATTR_VALS.include? value then false
|
50
|
+
elsif TRUE_ATTR_VALS.include? value then true
|
51
|
+
else value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def []= key, value
|
56
|
+
key, value = normalize(key), value.to_s
|
57
|
+
|
58
|
+
# This one can cause problems as looks there is no escaping strategy on the server side
|
59
|
+
#unless value == '"'
|
60
|
+
# single_quotes, double_quotes = value.scan('"').size, value.scan('""').size
|
61
|
+
# if (single_quotes > 0) and ((double_quotes == 0) or (single_quotes.to_f % double_quotes.to_f > 0))
|
62
|
+
# raise(NodeError, "Found unescaped double quotes inside attribute value")
|
63
|
+
# end
|
64
|
+
# @attributes[key] = value.gsub('"', '""')
|
65
|
+
#end
|
66
|
+
|
67
|
+
@attributes[key] = value
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def normalize name
|
73
|
+
name.to_s.strip
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
class NodeArray < Array
|
3
|
+
def [] key
|
4
|
+
unless key.is_a? Integer
|
5
|
+
return first[key] unless empty?
|
6
|
+
nil
|
7
|
+
else
|
8
|
+
super key
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def []= key, value
|
13
|
+
(key.is_a? Integer) ? super(key, value) : (first[key] = value) unless empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing method
|
17
|
+
nodes = inject([]) {|array, element| array + element.send(method) if element.kind_of? Node}
|
18
|
+
NodeArray.new.replace nodes
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_wml
|
22
|
+
inject('') {|string, item| string << "#{item}\n" if item.kind_of? Node}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
class Reader
|
3
|
+
def read_hsh hash, name = '', parent = nil
|
4
|
+
current_node = Node.new name, parent
|
5
|
+
hash.each {|key,val| (val.kind_of? Hash) ? current_node.add(read_hsh(val, key, current_node)) : current_node[key] = val}
|
6
|
+
current_node
|
7
|
+
end
|
8
|
+
|
9
|
+
def read_str string
|
10
|
+
parse string
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_uri uri
|
14
|
+
require 'open-uri'
|
15
|
+
require 'stringio'
|
16
|
+
require 'zlib'
|
17
|
+
|
18
|
+
begin
|
19
|
+
content = open(uri) {|u| u.read}
|
20
|
+
rescue
|
21
|
+
raise ReaderError, "Failed to open specified uri"
|
22
|
+
ensure
|
23
|
+
content = Zlib::GzipReader.new(StringIO.new(content)).read rescue content
|
24
|
+
end
|
25
|
+
|
26
|
+
parse content
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse content
|
32
|
+
current_node, node_stack = Node.new, []
|
33
|
+
multiline_mode, multiline_name, multiline_value = false, '', []
|
34
|
+
|
35
|
+
content.strip.each_line do |line|
|
36
|
+
line = line.chomp.extend StringExtensions
|
37
|
+
|
38
|
+
if multiline_mode
|
39
|
+
if line.end_of_attr?
|
40
|
+
multiline_value << line.rstrip.chomp('"')
|
41
|
+
multiline_value = multiline_value.join("\n")
|
42
|
+
current_node[multiline_name] = multiline_value
|
43
|
+
multiline_mode, multiline_name, multiline_value = false, '', []
|
44
|
+
else
|
45
|
+
multiline_value << line
|
46
|
+
end
|
47
|
+
elsif line.attribute?
|
48
|
+
if line.multiline_attr?
|
49
|
+
multiline_mode, multiline_name = true, line.attribute_name
|
50
|
+
multiline_value << line.attribute_value
|
51
|
+
else
|
52
|
+
current_node[line.attribute_name] = line.attribute_value
|
53
|
+
end
|
54
|
+
elsif line.opening_tag?
|
55
|
+
node_stack.push current_node
|
56
|
+
current_node = Node.create line.tag_name, node_stack.last
|
57
|
+
elsif line.closing_tag?
|
58
|
+
raise(ReaderError, "Opening tag '#{current_node.name}' does not match closing tag '#{line.tag_name}'") if current_node.name != line.tag_name
|
59
|
+
node_stack.last.add current_node
|
60
|
+
current_node = node_stack.pop
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
current_node
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
module StringExtensions
|
3
|
+
VALID_CHARS = '[A-Za-z0-9_]'
|
4
|
+
|
5
|
+
def attribute_name
|
6
|
+
@values[0]
|
7
|
+
end
|
8
|
+
|
9
|
+
def attribute_value
|
10
|
+
@values[1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def multiline_attr?
|
14
|
+
return true if include?('="') and (last_character != '"')
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def end_of_attr?
|
19
|
+
return true if (last_character == '"')
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def last_character
|
24
|
+
rstrip[-1,1]
|
25
|
+
end
|
26
|
+
|
27
|
+
def tag_name
|
28
|
+
@values[0]
|
29
|
+
end
|
30
|
+
|
31
|
+
def opening_tag?
|
32
|
+
wml_check(/^\[(#{VALID_CHARS}+?)\]$/)
|
33
|
+
end
|
34
|
+
|
35
|
+
def closing_tag?
|
36
|
+
wml_check(/^\[\/(#{VALID_CHARS}+?)\]$/)
|
37
|
+
end
|
38
|
+
|
39
|
+
def attribute?
|
40
|
+
# If there is unallowed char in fron of '=' line wont be considereda as an attribute
|
41
|
+
# Multiline attributes need to have double quotes next to equal sign
|
42
|
+
wml_check(/^(#{VALID_CHARS}+?)="?(.+?)"?$/)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def wml_check expression
|
48
|
+
matches = self.strip.match expression
|
49
|
+
|
50
|
+
if matches
|
51
|
+
@values = [matches[1], matches[2]]
|
52
|
+
return true
|
53
|
+
else
|
54
|
+
@values = [nil, nil]
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/weskit.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'weskit/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'weskit'
|
7
|
+
s.version = Weskit::VERSION
|
8
|
+
s.authors = ['f6p']
|
9
|
+
s.email = ['filip.pyda@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/f6p/weskit'
|
11
|
+
s.summary = 'Ruby utilies for BfW'
|
12
|
+
s.description = 'Pls do me a favor and do not look at sources if you dont have to. Thx!'
|
13
|
+
|
14
|
+
# s.rubyforge_project = 'weskit'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency 'rspec'
|
23
|
+
# s.add_runtime_dependency 'rest-client'
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weskit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- f6p
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-03-15 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Pls do me a favor and do not look at sources if you dont have to. Thx!
|
22
|
+
email:
|
23
|
+
- filip.pyda@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
33
|
+
- README
|
34
|
+
- Rakefile
|
35
|
+
- lib/weskit.rb
|
36
|
+
- lib/weskit/lobby.rb
|
37
|
+
- lib/weskit/lobby/bot.rb
|
38
|
+
- lib/weskit/lobby/bot_error.rb
|
39
|
+
- lib/weskit/version.rb
|
40
|
+
- lib/weskit/wml.rb
|
41
|
+
- lib/weskit/wml/node.rb
|
42
|
+
- lib/weskit/wml/node_array.rb
|
43
|
+
- lib/weskit/wml/node_error.rb
|
44
|
+
- lib/weskit/wml/reader.rb
|
45
|
+
- lib/weskit/wml/reader_error.rb
|
46
|
+
- lib/weskit/wml/string_extensions.rb
|
47
|
+
- weskit.gemspec
|
48
|
+
homepage: https://github.com/f6p/weskit
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.11
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Ruby utilies for BfW
|
81
|
+
test_files: []
|
82
|
+
|