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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 82cf74ba07e716f77a42f8d00a1c2c7a006ab1ee
4
- data.tar.gz: b1de6531791664de94bbb57c54ef4cefcab49bea
3
+ metadata.gz: e46d3df87918f5012997b654c5f47a78ed4e19e3
4
+ data.tar.gz: 345f9359d29321c10a4a69cceec83c9468d1d8af
5
5
  SHA512:
6
- metadata.gz: 5be9ba342fc77edc1636966f36716f05b2f96ac50302162167223e140e33e034699b2d5dfd5bee5ba8006acc17bd2a196a5dae6fe7a9fdcd2a4ed4d2bb602e4f
7
- data.tar.gz: 0f1c1849a77d0557270709d704dd252b8f8398b1fe9cae9415e5a77d77753f6fe1d40bed8c26a3736d77788e3145ef04304e651ec1068e45148d5cb0d7cb4546
6
+ metadata.gz: c3f904facc7c604f718701924fa1b5809e1268eb442b55906800efcfb78c13dea30c324bc3ff432435ac2343c4e7d7a54d573cd70e419538c3e72ec35e1abf5e
7
+ data.tar.gz: 0628cfddb4b8e04c2bb34f18e9c17de8a29d151c7371af35918b022346f793a9e23fe5d38decd448160a18f6abe59ce2a9fe930dacb7e37bf9312ff3b4fb703c
@@ -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
- * Murur server > 1.2.4
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', 64738, 'Mumble Bot', 'password123')
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 ...>
@@ -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
@@ -7,21 +7,27 @@ module Mumble
7
7
  class NoSupportedCodec < StandardError; end
8
8
 
9
9
  class Client
10
- attr_reader :host, :port, :username, :password, :users, :channels
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
- @conn = Connection.new @host, @port
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 48000, 480, 1
180
+ @encoder = Opus::Encoder.new @config.sample_rate, @config.sample_rate / 100, 1
159
181
  @encoder.vbr_rate = 0 # CBR
160
- @encoder.bitrate = 32000 # 32 kbit/s
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 -o}.strip,
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
- id = case channel
186
- when Messages::ChannelState
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
- id = case user
200
- when Messages::UserState
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.sysclose
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
@@ -1,3 +1,3 @@
1
1
  module Mumble
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
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.1
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-23 00:00:00.000000000 Z
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