mumble-ruby 1.0.1 → 1.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.
- checksums.yaml +4 -4
- data/README.rdoc +31 -2
- data/lib/mumble-ruby.rb +25 -0
- data/lib/mumble-ruby/cert_manager.rb +67 -0
- data/lib/mumble-ruby/client.rb +39 -29
- data/lib/mumble-ruby/connection.rb +4 -2
- data/lib/mumble-ruby/img_reader.rb +39 -0
- data/lib/mumble-ruby/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e46d3df87918f5012997b654c5f47a78ed4e19e3
|
4
|
+
data.tar.gz: 345f9359d29321c10a4a69cceec83c9468d1d8af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3f904facc7c604f718701924fa1b5809e1268eb442b55906800efcfb78c13dea30c324bc3ff432435ac2343c4e7d7a54d573cd70e419538c3e72ec35e1abf5e
|
7
|
+
data.tar.gz: 0628cfddb4b8e04c2bb34f18e9c17de8a29d151c7371af35918b022346f793a9e23fe5d38decd448160a18f6abe59ce2a9fe930dacb7e37bf9312ff3b4fb703c
|
data/README.rdoc
CHANGED
@@ -16,12 +16,38 @@ There is huge room for improvement in this library and I am willing to accept al
|
|
16
16
|
|
17
17
|
* Ruby >= 2.1.0
|
18
18
|
* OPUS Audio Codec
|
19
|
-
*
|
19
|
+
* Murmur server > 1.2.4
|
20
|
+
|
21
|
+
== RECENT CHANGES:
|
22
|
+
|
23
|
+
* Added OPUS support
|
24
|
+
* Added more configuration options
|
25
|
+
* Added image text messages
|
26
|
+
* Added ssl cert auth
|
27
|
+
* Fixed several bugs
|
20
28
|
|
21
29
|
== BASIC USAGE:
|
22
30
|
|
31
|
+
# Configure all clients globally
|
32
|
+
Mumble.configure do |conf|
|
33
|
+
# sample rate of sound (48 khz recommended)
|
34
|
+
conf.sample_rate = 48000
|
35
|
+
|
36
|
+
# bitrate of sound (32 kbit/s recommended)
|
37
|
+
conf.bitrate = 32000
|
38
|
+
|
39
|
+
# directory to store user's ssl certs
|
40
|
+
conf.ssl_cert_opts[:cert_dir] = File.expand_path("./")
|
41
|
+
end
|
42
|
+
|
23
43
|
# Create client instance for your server
|
24
|
-
cli = Mumble::Client.new('localhost'
|
44
|
+
cli = Mumble::Client.new('localhost') do |conf|
|
45
|
+
conf.username = 'Mumble Bot'
|
46
|
+
conf.password = 'password123'
|
47
|
+
|
48
|
+
# Overwrite global config
|
49
|
+
conf.bitrate = 48000
|
50
|
+
end
|
25
51
|
# => #<Mumble::Client:0x00000003064fe8 @host="localhost", @port=64738, @username="Mumble Bot", @password="password123", @channels={}, @users={}, @callbacks={}>
|
26
52
|
|
27
53
|
# Set up some callbacks for when you recieve text messages
|
@@ -62,6 +88,9 @@ There is huge room for improvement in this library and I am willing to accept al
|
|
62
88
|
cli.text_user('perrym5', "Hello there, I'm a robot!")
|
63
89
|
# => 35
|
64
90
|
|
91
|
+
# Text an image to a channel
|
92
|
+
cli.text_channel_img('Chillen', '/path/to/image.jpg')
|
93
|
+
|
65
94
|
# Start streaming from a FIFO queue of raw PCM data
|
66
95
|
cli.stream_raw_audio('/tmp/mpd.fifo')
|
67
96
|
# => #<Mumble::AudioStream ...>
|
data/lib/mumble-ruby.rb
CHANGED
@@ -6,3 +6,28 @@ require 'mumble-ruby/connection.rb'
|
|
6
6
|
require 'mumble-ruby/client.rb'
|
7
7
|
require 'mumble-ruby/audio_stream.rb'
|
8
8
|
require 'mumble-ruby/packet_data_stream.rb'
|
9
|
+
require 'mumble-ruby/img_reader.rb'
|
10
|
+
require 'mumble-ruby/cert_manager.rb'
|
11
|
+
|
12
|
+
module Mumble
|
13
|
+
DEFAULTS = {
|
14
|
+
sample_rate: 48000,
|
15
|
+
bitrate: 32000,
|
16
|
+
ssl_cert_opts: {
|
17
|
+
cert_dir: File.expand_path("./"),
|
18
|
+
country_code: "US",
|
19
|
+
organization: "github.com",
|
20
|
+
organization_unit: "Engineering"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
def self.configuration
|
25
|
+
@configuration ||= Hashie::Mash.new(DEFAULTS)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.configure
|
29
|
+
yield(configuration) if block_given?
|
30
|
+
end
|
31
|
+
|
32
|
+
Thread.abort_on_exception = true
|
33
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Mumble
|
5
|
+
class CertManager
|
6
|
+
attr_reader :key, :cert
|
7
|
+
|
8
|
+
CERT_STRING = "/C=%s/O=%s/OU=%s/CN=%s"
|
9
|
+
|
10
|
+
def initialize(username, opts)
|
11
|
+
@cert_dir = File.join(opts[:cert_dir], "#{username.downcase}_cert")
|
12
|
+
@username = username
|
13
|
+
@opts = opts
|
14
|
+
|
15
|
+
FileUtils.mkdir_p @cert_dir
|
16
|
+
setup_key
|
17
|
+
setup_cert
|
18
|
+
end
|
19
|
+
|
20
|
+
[:private_key, :public_key, :cert].each do |sym|
|
21
|
+
define_method "#{sym}_path" do
|
22
|
+
File.join(@cert_dir, "#{sym}.pem")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def setup_key
|
28
|
+
if File.exists?(private_key_path)
|
29
|
+
@key ||= OpenSSL::PKey::RSA.new File.read(private_key_path)
|
30
|
+
else
|
31
|
+
@key ||= OpenSSL::PKey::RSA.new 2048
|
32
|
+
File.write private_key_path, key.to_pem
|
33
|
+
File.write public_key_path, key.public_key.to_pem
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup_cert
|
38
|
+
if File.exists?(cert_path)
|
39
|
+
@cert ||= OpenSSL::X509::Certificate.new File.read(cert_path)
|
40
|
+
else
|
41
|
+
@cert ||= OpenSSL::X509::Certificate.new
|
42
|
+
|
43
|
+
subject = CERT_STRING % [@opts[:country_code], @opts[:organization], @opts[:organization_unit], @username]
|
44
|
+
|
45
|
+
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
|
46
|
+
cert.not_before = Time.now
|
47
|
+
cert.not_after = Time.new + 365 * 24 * 60 * 60 * 5
|
48
|
+
cert.public_key = key.public_key
|
49
|
+
cert.serial = rand(65535) + 1
|
50
|
+
cert.version = 2
|
51
|
+
|
52
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
53
|
+
ef.subject_certificate = cert
|
54
|
+
ef.issuer_certificate = cert
|
55
|
+
|
56
|
+
cert.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
|
57
|
+
cert.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
|
58
|
+
cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
|
59
|
+
cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
|
60
|
+
|
61
|
+
cert.sign key, OpenSSL::Digest::SHA256.new
|
62
|
+
|
63
|
+
File.write cert_path, cert.to_pem
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/mumble-ruby/client.rb
CHANGED
@@ -7,21 +7,27 @@ module Mumble
|
|
7
7
|
class NoSupportedCodec < StandardError; end
|
8
8
|
|
9
9
|
class Client
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :users, :channels, :connected
|
11
11
|
|
12
12
|
CODEC_OPUS = 4
|
13
13
|
|
14
14
|
def initialize(host, port=64738, username="RubyClient", password="")
|
15
|
-
@host = host
|
16
|
-
@port = port
|
17
|
-
@username = username
|
18
|
-
@password = password
|
19
15
|
@users, @channels = {}, {}
|
20
16
|
@callbacks = Hash.new { |h, k| h[k] = [] }
|
17
|
+
@connected = false
|
18
|
+
|
19
|
+
@config = Mumble.configuration.dup.tap do |c|
|
20
|
+
c.host = host
|
21
|
+
c.port = port
|
22
|
+
c.username = username
|
23
|
+
c.password = password
|
24
|
+
end
|
25
|
+
yield(@config) if block_given?
|
21
26
|
end
|
22
27
|
|
23
28
|
def connect
|
24
|
-
|
29
|
+
cert_manager = CertManager.new(@config.username, @config.ssl_cert_opts)
|
30
|
+
@conn = Connection.new @config.host, @config.port, cert_manager
|
25
31
|
@conn.connect
|
26
32
|
|
27
33
|
create_encoder
|
@@ -38,6 +44,7 @@ module Mumble
|
|
38
44
|
@read_thread.kill
|
39
45
|
@ping_thread.kill
|
40
46
|
@conn.disconnect
|
47
|
+
@connected = false
|
41
48
|
end
|
42
49
|
|
43
50
|
def me
|
@@ -85,6 +92,11 @@ module Mumble
|
|
85
92
|
})
|
86
93
|
end
|
87
94
|
|
95
|
+
def text_user_img(user, file)
|
96
|
+
img = ImgReader.new file
|
97
|
+
text_user(user, img.to_msg)
|
98
|
+
end
|
99
|
+
|
88
100
|
def text_channel(channel, string)
|
89
101
|
send_text_message({
|
90
102
|
channel_id: [channel_id(channel)],
|
@@ -92,6 +104,11 @@ module Mumble
|
|
92
104
|
})
|
93
105
|
end
|
94
106
|
|
107
|
+
def text_channel_img(channel, file)
|
108
|
+
img = ImgReader.new file
|
109
|
+
text_channel(channel, img.to_msg)
|
110
|
+
end
|
111
|
+
|
95
112
|
def user_stats(user)
|
96
113
|
send_user_stats session: user_session(user)
|
97
114
|
end
|
@@ -104,16 +121,19 @@ module Mumble
|
|
104
121
|
channels.values.find { |c| c.name == name }
|
105
122
|
end
|
106
123
|
|
124
|
+
def on_connected(&block)
|
125
|
+
@callbacks[:connected] << block
|
126
|
+
end
|
127
|
+
|
107
128
|
private
|
108
129
|
def spawn_thread(sym)
|
109
|
-
Thread.abort_on_exception = true
|
110
130
|
Thread.new { loop { send sym } }
|
111
131
|
end
|
112
132
|
|
113
133
|
def read
|
114
134
|
message = @conn.read_message
|
115
135
|
sym = message.class.to_s.demodulize.underscore.to_sym
|
116
|
-
run_callbacks sym, message
|
136
|
+
run_callbacks sym, Hashie::Mash.new(message.to_hash)
|
117
137
|
end
|
118
138
|
|
119
139
|
def ping
|
@@ -128,6 +148,8 @@ module Mumble
|
|
128
148
|
def init_callbacks
|
129
149
|
on_server_sync do |message|
|
130
150
|
@session = message.session
|
151
|
+
@connected = true
|
152
|
+
@callbacks[:connected].each { |c| c.call }
|
131
153
|
end
|
132
154
|
on_channel_state do |message|
|
133
155
|
if channel = channels[message.channel_id]
|
@@ -155,24 +177,24 @@ module Mumble
|
|
155
177
|
end
|
156
178
|
|
157
179
|
def create_encoder
|
158
|
-
@encoder = Opus::Encoder.new
|
180
|
+
@encoder = Opus::Encoder.new @config.sample_rate, @config.sample_rate / 100, 1
|
159
181
|
@encoder.vbr_rate = 0 # CBR
|
160
|
-
@encoder.bitrate =
|
182
|
+
@encoder.bitrate = @config.bitrate
|
161
183
|
end
|
162
184
|
|
163
185
|
def version_exchange
|
164
186
|
send_version({
|
165
187
|
version: encode_version(1, 2, 5),
|
166
188
|
release: "mumble-ruby #{Mumble::VERSION}",
|
167
|
-
os: %x{uname -
|
189
|
+
os: %x{uname -s}.strip,
|
168
190
|
os_version: %x{uname -v}.strip
|
169
191
|
})
|
170
192
|
end
|
171
193
|
|
172
194
|
def authenticate
|
173
195
|
send_authenticate({
|
174
|
-
username: @username,
|
175
|
-
password: @password,
|
196
|
+
username: @config.username,
|
197
|
+
password: @config.password,
|
176
198
|
opus: true
|
177
199
|
})
|
178
200
|
end
|
@@ -182,28 +204,16 @@ module Mumble
|
|
182
204
|
end
|
183
205
|
|
184
206
|
def channel_id(channel)
|
185
|
-
|
186
|
-
|
187
|
-
channel.channel_id
|
188
|
-
when Fixnum
|
189
|
-
channel
|
190
|
-
when String
|
191
|
-
find_channel(channel).channel_id
|
192
|
-
end
|
207
|
+
channel = find_channel(channel) if channel.is_a? String
|
208
|
+
id = channel.respond_to?(:channel_id) ? channel.channel_id : channel
|
193
209
|
|
194
210
|
raise ChannelNotFound unless @channels.has_key? id
|
195
211
|
id
|
196
212
|
end
|
197
213
|
|
198
214
|
def user_session(user)
|
199
|
-
|
200
|
-
|
201
|
-
user.session
|
202
|
-
when Fixnum
|
203
|
-
user
|
204
|
-
when String
|
205
|
-
find_user(user).session
|
206
|
-
end
|
215
|
+
user = find_user(user) if user.is_a? String
|
216
|
+
id = user.respond_to?(:session) ? user.session : user
|
207
217
|
|
208
218
|
raise UserNotFound unless @users.has_key? id
|
209
219
|
id
|
@@ -4,22 +4,24 @@ require 'thread'
|
|
4
4
|
|
5
5
|
module Mumble
|
6
6
|
class Connection
|
7
|
-
def initialize(host, port)
|
7
|
+
def initialize(host, port, cert_manager)
|
8
8
|
@host = host
|
9
9
|
@port = port
|
10
|
+
@cert_manager = cert_manager
|
10
11
|
@write_lock = Mutex.new
|
11
12
|
end
|
12
13
|
|
13
14
|
def connect
|
14
15
|
context = OpenSSL::SSL::SSLContext.new(:TLSv1)
|
15
16
|
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
17
|
+
[:key, :cert].each { |s| context.send("#{s}=", @cert_manager.send(s)) }
|
16
18
|
tcp_sock = TCPSocket.new @host, @port
|
17
19
|
@sock = OpenSSL::SSL::SSLSocket.new tcp_sock, context
|
18
20
|
@sock.connect
|
19
21
|
end
|
20
22
|
|
21
23
|
def disconnect
|
22
|
-
@sock.
|
24
|
+
@sock.close
|
23
25
|
end
|
24
26
|
|
25
27
|
def read_message
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Mumble
|
4
|
+
class UnsupportedImgFormat < StandardError
|
5
|
+
def initialize
|
6
|
+
super "Image format must be one of the following: #{ImgReader::FORMATS}"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ImgTooLarge < StandardError
|
11
|
+
def initialize
|
12
|
+
super "Image must be smaller than 128 kB"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ImgReader
|
17
|
+
attr_reader :file
|
18
|
+
FORMATS = %w(png jpg jpeg svg)
|
19
|
+
|
20
|
+
def initialize(file)
|
21
|
+
@file = file
|
22
|
+
raise LoadError.new("#{file} not found") unless File.exists? file
|
23
|
+
raise UnsupportedImgFormat unless FORMATS.include? ext
|
24
|
+
raise ImgTooLarge unless File.size(file) <= 128 * 1024
|
25
|
+
end
|
26
|
+
|
27
|
+
def ext
|
28
|
+
@ext ||= File.extname(@file)[1..-1]
|
29
|
+
end
|
30
|
+
|
31
|
+
def data
|
32
|
+
@data ||= File.read @file
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_msg
|
36
|
+
"<img src='data:image/#{ext};base64,#{Base64.encode64(data)}'/>"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/mumble-ruby/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mumble-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Perry
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -82,8 +82,10 @@ files:
|
|
82
82
|
- Rakefile
|
83
83
|
- lib/mumble-ruby.rb
|
84
84
|
- lib/mumble-ruby/audio_stream.rb
|
85
|
+
- lib/mumble-ruby/cert_manager.rb
|
85
86
|
- lib/mumble-ruby/client.rb
|
86
87
|
- lib/mumble-ruby/connection.rb
|
88
|
+
- lib/mumble-ruby/img_reader.rb
|
87
89
|
- lib/mumble-ruby/messages.rb
|
88
90
|
- lib/mumble-ruby/mumble.proto
|
89
91
|
- lib/mumble-ruby/packet_data_stream.rb
|