mumble-ruby 0.0.1
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 +17 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/README.rdoc +76 -0
- data/Rakefile +2 -0
- data/lib/mumble-ruby.rb +7 -0
- data/lib/mumble-ruby/audio_stream.rb +54 -0
- data/lib/mumble-ruby/client.rb +210 -0
- data/lib/mumble-ruby/connection.rb +83 -0
- data/lib/mumble-ruby/messages.rb +344 -0
- data/lib/mumble-ruby/mumble.proto +276 -0
- data/lib/mumble-ruby/packet_data_stream.rb +157 -0
- data/lib/mumble-ruby/version.rb +3 -0
- data/mumble-ruby.gemspec +22 -0
- metadata +92 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm use --create ruby-1.9.3-p0@mumble_ruby_gem
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
mumble-ruby
|
|
2
|
+
http://www.github.com/perrym5/mumble-ruby
|
|
3
|
+
|
|
4
|
+
== DESCRIPTION:
|
|
5
|
+
|
|
6
|
+
Mumble-Ruby is a headless client for the Mumble VOIP application.
|
|
7
|
+
Mumble-Ruby provides the ability to write scripts and bots which interact with mumble servers through a simple DSL.
|
|
8
|
+
Mumble-Ruby also has the ability to stream raw audio from a fifo pipe (mpd) to the mumble server.
|
|
9
|
+
There is huge room for improvement in this library and I am willing to accept all sorts of pull requests so please do.
|
|
10
|
+
|
|
11
|
+
== INSTALL:
|
|
12
|
+
|
|
13
|
+
[sudo] gem install mumble-ruby
|
|
14
|
+
|
|
15
|
+
== REQUIREMENTS:
|
|
16
|
+
|
|
17
|
+
* Ruby >= 1.9.2
|
|
18
|
+
* CELT Audio Codec Library version 0.7
|
|
19
|
+
|
|
20
|
+
== BASIC USAGE:
|
|
21
|
+
|
|
22
|
+
# Create client instance for your server
|
|
23
|
+
cli = Mumble::Client.new('localhost', 64738, 'Mumble Bot', 'password123')
|
|
24
|
+
# => #<Mumble::Client:0x00000003064fe8 @host="localhost", @port=64738, @username="Mumble Bot", @password="password123", @channels={}, @users={}, @callbacks={}>
|
|
25
|
+
|
|
26
|
+
# Set up some callbacks for when you recieve text messages
|
|
27
|
+
# There are callbacks for every Mumble Protocol Message that a client can recieve
|
|
28
|
+
# For a reference on those, see the linked PDF at the bottom of the README.
|
|
29
|
+
cli.on_text_message do |msg|
|
|
30
|
+
puts msg.message
|
|
31
|
+
end
|
|
32
|
+
# => [#<Proc:0x0000000346e5f8@(irb):2>]
|
|
33
|
+
|
|
34
|
+
# Initiate the connection to the client
|
|
35
|
+
cli.connect
|
|
36
|
+
# => #<Thread:0x000000033d7388 run>
|
|
37
|
+
|
|
38
|
+
# Mute and Deafen yourself
|
|
39
|
+
cli.mute
|
|
40
|
+
cli.deafen
|
|
41
|
+
|
|
42
|
+
# Join the channel titled "Chillen" (this returns the number of bytes written to the socket)
|
|
43
|
+
cli.join_channel('Chillen')
|
|
44
|
+
# => 11
|
|
45
|
+
|
|
46
|
+
# Get a list of channels
|
|
47
|
+
cli.channels
|
|
48
|
+
# Returns a hash of channel_id: ChannelState Messages
|
|
49
|
+
|
|
50
|
+
# Join Channel using ID
|
|
51
|
+
cli.join_channel(0)
|
|
52
|
+
|
|
53
|
+
# Join Channel using ChannelState Message
|
|
54
|
+
cli.join_channel(cli.channels[0])
|
|
55
|
+
|
|
56
|
+
# Get a list of users
|
|
57
|
+
cli.users
|
|
58
|
+
# Returns a hash of session_id: UserState Messages
|
|
59
|
+
|
|
60
|
+
# Text user
|
|
61
|
+
cli.text_user('perrym5', "Hello there, I'm a robot!")
|
|
62
|
+
# => 35
|
|
63
|
+
|
|
64
|
+
# Start streaming from a FIFO queue of raw PCM data
|
|
65
|
+
cli.stream_raw_audio('/tmp/mpd.fifo')
|
|
66
|
+
# => #<Mumble::AudioStream ...>
|
|
67
|
+
|
|
68
|
+
# Safely disconnect
|
|
69
|
+
cli.disconnect
|
|
70
|
+
# => nil
|
|
71
|
+
|
|
72
|
+
== MUMBLE PROTOCOL:
|
|
73
|
+
|
|
74
|
+
The documentation for Mumble's control and voice protocol is a good reference for using this client as all of the callbacks
|
|
75
|
+
are based on the types of messages the Mumble uses to accomplish its tasks.
|
|
76
|
+
You can see it here[https://github.com/mumble-voip/mumble/raw/master/doc/mumble-protocol.pdf].
|
data/Rakefile
ADDED
data/lib/mumble-ruby.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Mumble
|
|
2
|
+
class AudioStream
|
|
3
|
+
def initialize(type, target, encoder, file, connection)
|
|
4
|
+
@type = type
|
|
5
|
+
@target = target
|
|
6
|
+
@encoder = encoder
|
|
7
|
+
@file = File.open(file, 'rb')
|
|
8
|
+
@conn = connection
|
|
9
|
+
@seq = 0
|
|
10
|
+
@num_frames = 6
|
|
11
|
+
@compressed_size = [@encoder.vbr_rate / 800, 127].min
|
|
12
|
+
@pds = PacketDataStream.new
|
|
13
|
+
|
|
14
|
+
@queue = Queue.new
|
|
15
|
+
@producer = spawn_thread :produce
|
|
16
|
+
@consumer = spawn_thread :consume
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
def packet_header
|
|
21
|
+
((@type << 5) | @target).chr
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def produce
|
|
25
|
+
pcm_data = @file.read(@encoder.frame_size * 2)
|
|
26
|
+
@queue << @encoder.encode(pcm_data, @compressed_size)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def consume
|
|
30
|
+
@seq %= 1000000 # Keep sequence number reasonable for long runs
|
|
31
|
+
|
|
32
|
+
@pds.rewind
|
|
33
|
+
@seq += @num_frames
|
|
34
|
+
@pds.put_int @seq
|
|
35
|
+
|
|
36
|
+
@num_frames.times do |i|
|
|
37
|
+
frame = @queue.pop
|
|
38
|
+
len = frame.size
|
|
39
|
+
len = len | 0x80 if i < @num_frames - 1
|
|
40
|
+
@pds.append len
|
|
41
|
+
@pds.append_block frame
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
size = @pds.size
|
|
45
|
+
@pds.rewind
|
|
46
|
+
data = [packet_header, @pds.get_block(size)].flatten.join
|
|
47
|
+
@conn.send_udp_packet data
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def spawn_thread(sym)
|
|
51
|
+
Thread.new { loop { send sym } }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module Mumble
|
|
4
|
+
class ChannelNotFound < StandardError; end
|
|
5
|
+
class UserNotFound < StandardError; end
|
|
6
|
+
class NoSupportedCodec < StandardError; end
|
|
7
|
+
|
|
8
|
+
CODEC_ALPHA = 0
|
|
9
|
+
CODEC_BETA = 3
|
|
10
|
+
|
|
11
|
+
class Client
|
|
12
|
+
attr_reader :host, :port, :username, :password, :users, :channels
|
|
13
|
+
|
|
14
|
+
def initialize(host, port=64738, username="Ruby Client", password="")
|
|
15
|
+
@host = host
|
|
16
|
+
@port = port
|
|
17
|
+
@username = username
|
|
18
|
+
@password = password
|
|
19
|
+
@users, @channels = {}, {}
|
|
20
|
+
@callbacks = Hash.new { |h, k| h[k] = [] }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def connect
|
|
24
|
+
@conn = Connection.new @host, @port
|
|
25
|
+
@conn.connect
|
|
26
|
+
|
|
27
|
+
create_encoder
|
|
28
|
+
version_exchange
|
|
29
|
+
authenticate
|
|
30
|
+
init_callbacks
|
|
31
|
+
|
|
32
|
+
@read_thread = spawn_thread :read
|
|
33
|
+
@ping_thread = spawn_thread :ping
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def disconnect
|
|
37
|
+
@encoder.destroy
|
|
38
|
+
@read_thread.kill
|
|
39
|
+
@ping_thread.kill
|
|
40
|
+
@conn.disconnect
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def me
|
|
44
|
+
@users[@session]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def current_channel
|
|
48
|
+
@channels[me.channel_id]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def stream_raw_audio(file)
|
|
52
|
+
raise NoSupportedCodec unless @codec
|
|
53
|
+
AudioStream.new(@codec, 0, @encoder, file, @conn)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
Messages.all_types.each do |msg_type|
|
|
57
|
+
define_method "on_#{msg_type}" do |&block|
|
|
58
|
+
@callbacks[msg_type] << block
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
define_method "send_#{msg_type}" do |opts|
|
|
62
|
+
@conn.send_message(msg_type, opts)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def mute(bool=true)
|
|
67
|
+
send_user_state self_mute: bool
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def deafen(bool=true)
|
|
71
|
+
send_user_state self_deaf: bool
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def join_channel(channel)
|
|
75
|
+
send_user_state({
|
|
76
|
+
session: me.session,
|
|
77
|
+
channel_id: channel_id(channel)
|
|
78
|
+
})
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def text_user(user, string)
|
|
82
|
+
send_text_message({
|
|
83
|
+
session: [user_session(user)],
|
|
84
|
+
message: string
|
|
85
|
+
})
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def text_channel(channel, string)
|
|
89
|
+
send_text_message({
|
|
90
|
+
channel_id: [channel_id(channel)],
|
|
91
|
+
message: string
|
|
92
|
+
})
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def user_stats(user)
|
|
96
|
+
send_user_stats session: user_session(user)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def find_user(name)
|
|
100
|
+
@users.values.find { |u| u.name == name }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def find_channel(name)
|
|
104
|
+
@channels.values.find { |u| u.name == name }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
def spawn_thread(sym)
|
|
109
|
+
Thread.new { loop { send sym } }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def read
|
|
113
|
+
message = @conn.read_message
|
|
114
|
+
sym = message.class.to_s.demodulize.underscore.to_sym
|
|
115
|
+
run_callbacks sym, message
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def ping
|
|
119
|
+
send_ping timestamp: Time.now.to_i
|
|
120
|
+
sleep(20)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def run_callbacks(sym, *args)
|
|
124
|
+
@callbacks[sym].each { |c| c.call *args }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def init_callbacks
|
|
128
|
+
on_server_sync do |message|
|
|
129
|
+
@session = message.session
|
|
130
|
+
end
|
|
131
|
+
on_channel_state do |message|
|
|
132
|
+
@channels[message.channel_id] = message
|
|
133
|
+
end
|
|
134
|
+
on_channel_remove do |message|
|
|
135
|
+
@channels.delete(message.channel_id)
|
|
136
|
+
end
|
|
137
|
+
on_user_state do |message|
|
|
138
|
+
@users[message.session] = message
|
|
139
|
+
end
|
|
140
|
+
on_user_remove do |message|
|
|
141
|
+
@users.delete(message.session)
|
|
142
|
+
end
|
|
143
|
+
on_codec_version do |message|
|
|
144
|
+
codec_negotiation(message.alpha, message.beta)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def create_encoder
|
|
149
|
+
@encoder = Celt::Encoder.new 48000, 480, 1
|
|
150
|
+
@encoder.prediction_request = 0
|
|
151
|
+
@encoder.vbr_rate = 60000
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def version_exchange
|
|
155
|
+
send_version({
|
|
156
|
+
version: encode_version(1, 2, 3),
|
|
157
|
+
release: "mumble-ruby #{Mumble::VERSION}",
|
|
158
|
+
os: %x{uname -o}.strip,
|
|
159
|
+
os_version: %x{uname -v}.strip
|
|
160
|
+
})
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def authenticate
|
|
164
|
+
send_authenticate({
|
|
165
|
+
username: @username,
|
|
166
|
+
password: @password,
|
|
167
|
+
celt_versions: [@encoder.bitstream_version]
|
|
168
|
+
})
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def codec_negotiation(alpha, beta)
|
|
172
|
+
@codec = case @encoder.bitstream_version
|
|
173
|
+
when alpha then Mumble::CODEC_ALPHA
|
|
174
|
+
when beta then Mumble::CODEC_BETA
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def channel_id(channel)
|
|
179
|
+
id = case channel
|
|
180
|
+
when Messages::ChannelState
|
|
181
|
+
channel.channel_id
|
|
182
|
+
when Fixnum
|
|
183
|
+
channel
|
|
184
|
+
when String
|
|
185
|
+
find_channel(channel).channel_id
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
raise ChannelNotFound unless @channels.has_key? id
|
|
189
|
+
id
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def user_session(user)
|
|
193
|
+
id = case user
|
|
194
|
+
when Messages::ChannelState
|
|
195
|
+
user.session
|
|
196
|
+
when Fixnum
|
|
197
|
+
user
|
|
198
|
+
when String
|
|
199
|
+
find_user(user).session
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
raise UserNotFound unless @users.has_key? id
|
|
203
|
+
id
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def encode_version(major, minor, patch)
|
|
207
|
+
(major << 16) | (minor << 8) | (patch & 0xFF)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
require 'openssl'
|
|
3
|
+
require 'thread'
|
|
4
|
+
|
|
5
|
+
module Mumble
|
|
6
|
+
class Connection
|
|
7
|
+
def initialize(host, port)
|
|
8
|
+
@host = host
|
|
9
|
+
@port = port
|
|
10
|
+
@write_lock = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def connect
|
|
14
|
+
context = OpenSSL::SSL::SSLContext.new
|
|
15
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
16
|
+
tcp_sock = TCPSocket.new @host, @port
|
|
17
|
+
@sock = OpenSSL::SSL::SSLSocket.new tcp_sock, context
|
|
18
|
+
@sock.connect
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def disconnect
|
|
22
|
+
@sock.sysclose
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def read_message
|
|
26
|
+
header = read_data 6
|
|
27
|
+
type, len = header.unpack Messages::HEADER_FORMAT
|
|
28
|
+
data = read_data len
|
|
29
|
+
if type == message_type(:udp_tunnel)
|
|
30
|
+
# UDP Packet -- No Protobuf
|
|
31
|
+
message = message_class(:udp_tunnel).new
|
|
32
|
+
message.packet = data
|
|
33
|
+
else
|
|
34
|
+
message = message_raw type, data
|
|
35
|
+
end
|
|
36
|
+
message
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def send_udp_packet(packet)
|
|
40
|
+
header = [message_type(:udp_tunnel), packet.length].pack Messages::HEADER_FORMAT
|
|
41
|
+
send_data(header + packet)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def send_message(sym, attrs)
|
|
45
|
+
type, klass = message(sym)
|
|
46
|
+
message = klass.new
|
|
47
|
+
attrs.each { |k, v| message.send("#{k}=", v) }
|
|
48
|
+
serial = message.serialize_to_string
|
|
49
|
+
header = [type, serial.size].pack Messages::HEADER_FORMAT
|
|
50
|
+
send_data(header + serial)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
def send_data(data)
|
|
55
|
+
@write_lock.synchronize do
|
|
56
|
+
@sock.syswrite data
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def read_data(len)
|
|
61
|
+
@sock.sysread len
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def message(obj)
|
|
65
|
+
return message_type(obj), message_class(obj)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def message_type(obj)
|
|
69
|
+
if obj.is_a? Protobuf::Message
|
|
70
|
+
obj = obj.class.to_s.demodulize.underscore.to_sym
|
|
71
|
+
end
|
|
72
|
+
Messages.sym_to_type(obj)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def message_class(obj)
|
|
76
|
+
Messages.type_to_class(message_type(obj))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def message_raw(type, data)
|
|
80
|
+
Messages.raw_to_obj(type, data)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
### Generated by rprotoc. DO NOT EDIT!
|
|
2
|
+
### <proto file: Mumble.proto>
|
|
3
|
+
|
|
4
|
+
require 'protobuf/message/message'
|
|
5
|
+
require 'protobuf/message/enum'
|
|
6
|
+
require 'protobuf/message/service'
|
|
7
|
+
require 'protobuf/message/extend'
|
|
8
|
+
|
|
9
|
+
module Mumble
|
|
10
|
+
module Messages
|
|
11
|
+
::Protobuf::OPTIONS[:optimize_for] = :SPEED
|
|
12
|
+
HEADER_FORMAT = "nN"
|
|
13
|
+
|
|
14
|
+
@@sym_to_type = {
|
|
15
|
+
version: 0,
|
|
16
|
+
udp_tunnel: 1,
|
|
17
|
+
authenticate: 2,
|
|
18
|
+
ping: 3,
|
|
19
|
+
reject: 4,
|
|
20
|
+
server_sync: 5,
|
|
21
|
+
channel_remove: 6,
|
|
22
|
+
channel_state: 7,
|
|
23
|
+
user_remove: 8,
|
|
24
|
+
user_state: 9,
|
|
25
|
+
ban_list: 10,
|
|
26
|
+
text_message: 11,
|
|
27
|
+
permission_denied: 12,
|
|
28
|
+
acl: 13,
|
|
29
|
+
query_users: 14,
|
|
30
|
+
crypt_setup: 15,
|
|
31
|
+
context_action_add: 16,
|
|
32
|
+
context_action: 17,
|
|
33
|
+
user_list: 18,
|
|
34
|
+
voice_target: 19,
|
|
35
|
+
permission_query: 20,
|
|
36
|
+
codec_version: 21,
|
|
37
|
+
user_stats: 22,
|
|
38
|
+
request_blob: 23,
|
|
39
|
+
server_config: 24
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@@type_to_sym = @@sym_to_type.invert
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
def all_types
|
|
46
|
+
return @@sym_to_type.keys
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def sym_to_type(sym)
|
|
50
|
+
@@sym_to_type[sym]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def type_to_class(type)
|
|
54
|
+
const_get(@@type_to_sym[type].to_s.camelcase)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def raw_to_obj(type, data)
|
|
58
|
+
message = type_to_class(type).new
|
|
59
|
+
message.parse_from_string(data)
|
|
60
|
+
message
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Version < ::Protobuf::Message
|
|
65
|
+
defined_in __FILE__
|
|
66
|
+
optional :uint32, :version, 1
|
|
67
|
+
optional :string, :release, 2
|
|
68
|
+
optional :string, :os, 3
|
|
69
|
+
optional :string, :os_version, 4
|
|
70
|
+
end
|
|
71
|
+
class UdpTunnel < ::Protobuf::Message
|
|
72
|
+
defined_in __FILE__
|
|
73
|
+
required :bytes, :packet, 1
|
|
74
|
+
end
|
|
75
|
+
class Authenticate < ::Protobuf::Message
|
|
76
|
+
defined_in __FILE__
|
|
77
|
+
optional :string, :username, 1
|
|
78
|
+
optional :string, :password, 2
|
|
79
|
+
repeated :string, :tokens, 3
|
|
80
|
+
repeated :int32, :celt_versions, 4
|
|
81
|
+
end
|
|
82
|
+
class Ping < ::Protobuf::Message
|
|
83
|
+
defined_in __FILE__
|
|
84
|
+
optional :uint64, :timestamp, 1
|
|
85
|
+
optional :uint32, :good, 2
|
|
86
|
+
optional :uint32, :late, 3
|
|
87
|
+
optional :uint32, :lost, 4
|
|
88
|
+
optional :uint32, :resync, 5
|
|
89
|
+
optional :uint32, :udp_packets, 6
|
|
90
|
+
optional :uint32, :tcp_packets, 7
|
|
91
|
+
optional :float, :udp_ping_avg, 8
|
|
92
|
+
optional :float, :udp_ping_var, 9
|
|
93
|
+
optional :float, :tcp_ping_avg, 10
|
|
94
|
+
optional :float, :tcp_ping_var, 11
|
|
95
|
+
end
|
|
96
|
+
class Reject < ::Protobuf::Message
|
|
97
|
+
defined_in __FILE__
|
|
98
|
+
class RejectType < ::Protobuf::Enum
|
|
99
|
+
defined_in __FILE__
|
|
100
|
+
None = value(:None, 0)
|
|
101
|
+
WrongVersion = value(:WrongVersion, 1)
|
|
102
|
+
InvalidUsername = value(:InvalidUsername, 2)
|
|
103
|
+
WrongUserPW = value(:WrongUserPW, 3)
|
|
104
|
+
WrongServerPW = value(:WrongServerPW, 4)
|
|
105
|
+
UsernameInUse = value(:UsernameInUse, 5)
|
|
106
|
+
ServerFull = value(:ServerFull, 6)
|
|
107
|
+
NoCertificate = value(:NoCertificate, 7)
|
|
108
|
+
end
|
|
109
|
+
optional :RejectType, :type, 1
|
|
110
|
+
optional :string, :reason, 2
|
|
111
|
+
end
|
|
112
|
+
class ServerConfig < ::Protobuf::Message
|
|
113
|
+
defined_in __FILE__
|
|
114
|
+
optional :uint32, :max_bandwidth, 1
|
|
115
|
+
optional :string, :welcome_text, 2
|
|
116
|
+
optional :bool, :allow_html, 3
|
|
117
|
+
optional :uint32, :message_length, 4
|
|
118
|
+
optional :uint32, :image_message_length, 5
|
|
119
|
+
end
|
|
120
|
+
class ServerSync < ::Protobuf::Message
|
|
121
|
+
defined_in __FILE__
|
|
122
|
+
optional :uint32, :session, 1
|
|
123
|
+
optional :uint32, :max_bandwidth, 2
|
|
124
|
+
optional :string, :welcome_text, 3
|
|
125
|
+
optional :uint64, :permissions, 4
|
|
126
|
+
end
|
|
127
|
+
class ChannelRemove < ::Protobuf::Message
|
|
128
|
+
defined_in __FILE__
|
|
129
|
+
required :uint32, :channel_id, 1
|
|
130
|
+
end
|
|
131
|
+
class ChannelState < ::Protobuf::Message
|
|
132
|
+
defined_in __FILE__
|
|
133
|
+
optional :uint32, :channel_id, 1
|
|
134
|
+
optional :uint32, :parent, 2
|
|
135
|
+
optional :string, :name, 3
|
|
136
|
+
repeated :uint32, :links, 4
|
|
137
|
+
optional :string, :description, 5
|
|
138
|
+
repeated :uint32, :links_add, 6
|
|
139
|
+
repeated :uint32, :links_remove, 7
|
|
140
|
+
optional :bool, :temporary, 8, :default => false
|
|
141
|
+
optional :int32, :position, 9, :default => 0
|
|
142
|
+
optional :bytes, :description_hash, 10
|
|
143
|
+
end
|
|
144
|
+
class UserRemove < ::Protobuf::Message
|
|
145
|
+
defined_in __FILE__
|
|
146
|
+
required :uint32, :session, 1
|
|
147
|
+
optional :uint32, :actor, 2
|
|
148
|
+
optional :string, :reason, 3
|
|
149
|
+
optional :bool, :ban, 4
|
|
150
|
+
end
|
|
151
|
+
class UserState < ::Protobuf::Message
|
|
152
|
+
defined_in __FILE__
|
|
153
|
+
optional :uint32, :session, 1
|
|
154
|
+
optional :uint32, :actor, 2
|
|
155
|
+
optional :string, :name, 3
|
|
156
|
+
optional :uint32, :user_id, 4
|
|
157
|
+
optional :uint32, :channel_id, 5
|
|
158
|
+
optional :bool, :mute, 6
|
|
159
|
+
optional :bool, :deaf, 7
|
|
160
|
+
optional :bool, :suppress, 8
|
|
161
|
+
optional :bool, :self_mute, 9
|
|
162
|
+
optional :bool, :self_deaf, 10
|
|
163
|
+
optional :bytes, :texture, 11
|
|
164
|
+
optional :bytes, :plugin_context, 12
|
|
165
|
+
optional :string, :plugin_identity, 13
|
|
166
|
+
optional :string, :comment, 14
|
|
167
|
+
optional :string, :hash, 15
|
|
168
|
+
optional :bytes, :comment_hash, 16
|
|
169
|
+
optional :bytes, :texture_hash, 17
|
|
170
|
+
optional :bool, :priority_speaker, 18
|
|
171
|
+
optional :bool, :recording, 19
|
|
172
|
+
end
|
|
173
|
+
class BanList < ::Protobuf::Message
|
|
174
|
+
defined_in __FILE__
|
|
175
|
+
class BanEntry < ::Protobuf::Message
|
|
176
|
+
defined_in __FILE__
|
|
177
|
+
required :bytes, :address, 1
|
|
178
|
+
required :uint32, :mask, 2
|
|
179
|
+
optional :string, :name, 3
|
|
180
|
+
optional :string, :hash, 4
|
|
181
|
+
optional :string, :reason, 5
|
|
182
|
+
optional :string, :start, 6
|
|
183
|
+
optional :uint32, :duration, 7
|
|
184
|
+
end
|
|
185
|
+
repeated :BanEntry, :bans, 1
|
|
186
|
+
optional :bool, :query, 2, :default => false
|
|
187
|
+
end
|
|
188
|
+
class TextMessage < ::Protobuf::Message
|
|
189
|
+
defined_in __FILE__
|
|
190
|
+
optional :uint32, :actor, 1
|
|
191
|
+
repeated :uint32, :session, 2
|
|
192
|
+
repeated :uint32, :channel_id, 3
|
|
193
|
+
repeated :uint32, :tree_id, 4
|
|
194
|
+
required :string, :message, 5
|
|
195
|
+
end
|
|
196
|
+
class PermissionDenied < ::Protobuf::Message
|
|
197
|
+
defined_in __FILE__
|
|
198
|
+
class DenyType < ::Protobuf::Enum
|
|
199
|
+
defined_in __FILE__
|
|
200
|
+
Text = value(:Text, 0)
|
|
201
|
+
Permission = value(:Permission, 1)
|
|
202
|
+
SuperUser = value(:SuperUser, 2)
|
|
203
|
+
ChannelName = value(:ChannelName, 3)
|
|
204
|
+
TextTooLong = value(:TextTooLong, 4)
|
|
205
|
+
H9K = value(:H9K, 5)
|
|
206
|
+
TemporaryChannel = value(:TemporaryChannel, 6)
|
|
207
|
+
MissingCertificate = value(:MissingCertificate, 7)
|
|
208
|
+
UserName = value(:UserName, 8)
|
|
209
|
+
ChannelFull = value(:ChannelFull, 9)
|
|
210
|
+
end
|
|
211
|
+
optional :uint32, :permission, 1
|
|
212
|
+
optional :uint32, :channel_id, 2
|
|
213
|
+
optional :uint32, :session, 3
|
|
214
|
+
optional :string, :reason, 4
|
|
215
|
+
optional :DenyType, :type, 5
|
|
216
|
+
optional :string, :name, 6
|
|
217
|
+
end
|
|
218
|
+
class Acl < ::Protobuf::Message
|
|
219
|
+
defined_in __FILE__
|
|
220
|
+
class ChanGroup < ::Protobuf::Message
|
|
221
|
+
defined_in __FILE__
|
|
222
|
+
required :string, :name, 1
|
|
223
|
+
optional :bool, :inherited, 2, :default => true
|
|
224
|
+
optional :bool, :inherit, 3, :default => true
|
|
225
|
+
optional :bool, :inheritable, 4, :default => true
|
|
226
|
+
repeated :uint32, :add, 5
|
|
227
|
+
repeated :uint32, :remove, 6
|
|
228
|
+
repeated :uint32, :inherited_members, 7
|
|
229
|
+
end
|
|
230
|
+
class ChanACL < ::Protobuf::Message
|
|
231
|
+
defined_in __FILE__
|
|
232
|
+
optional :bool, :apply_here, 1, :default => true
|
|
233
|
+
optional :bool, :apply_subs, 2, :default => true
|
|
234
|
+
optional :bool, :inherited, 3, :default => true
|
|
235
|
+
optional :uint32, :user_id, 4
|
|
236
|
+
optional :string, :group, 5
|
|
237
|
+
optional :uint32, :grant, 6
|
|
238
|
+
optional :uint32, :deny, 7
|
|
239
|
+
end
|
|
240
|
+
required :uint32, :channel_id, 1
|
|
241
|
+
optional :bool, :inherit_acls, 2, :default => true
|
|
242
|
+
repeated :ChanGroup, :groups, 3
|
|
243
|
+
repeated :ChanACL, :acls, 4
|
|
244
|
+
optional :bool, :query, 5, :default => false
|
|
245
|
+
end
|
|
246
|
+
class QueryUsers < ::Protobuf::Message
|
|
247
|
+
defined_in __FILE__
|
|
248
|
+
repeated :uint32, :ids, 1
|
|
249
|
+
repeated :string, :names, 2
|
|
250
|
+
end
|
|
251
|
+
class CryptSetup < ::Protobuf::Message
|
|
252
|
+
defined_in __FILE__
|
|
253
|
+
optional :bytes, :key, 1
|
|
254
|
+
optional :bytes, :client_nonce, 2
|
|
255
|
+
optional :bytes, :server_nonce, 3
|
|
256
|
+
end
|
|
257
|
+
class ContextActionAdd < ::Protobuf::Message
|
|
258
|
+
defined_in __FILE__
|
|
259
|
+
class Context < ::Protobuf::Enum
|
|
260
|
+
defined_in __FILE__
|
|
261
|
+
Server = value(:Server, 1)
|
|
262
|
+
Channel = value(:Channel, 2)
|
|
263
|
+
User = value(:User, 4)
|
|
264
|
+
end
|
|
265
|
+
required :string, :action, 1
|
|
266
|
+
required :string, :text, 2
|
|
267
|
+
optional :uint32, :context, 3
|
|
268
|
+
end
|
|
269
|
+
class ContextAction < ::Protobuf::Message
|
|
270
|
+
defined_in __FILE__
|
|
271
|
+
optional :uint32, :session, 1
|
|
272
|
+
optional :uint32, :channel_id, 2
|
|
273
|
+
required :string, :action, 3
|
|
274
|
+
end
|
|
275
|
+
class UserList < ::Protobuf::Message
|
|
276
|
+
defined_in __FILE__
|
|
277
|
+
class User < ::Protobuf::Message
|
|
278
|
+
defined_in __FILE__
|
|
279
|
+
required :uint32, :user_id, 1
|
|
280
|
+
optional :string, :name, 2
|
|
281
|
+
end
|
|
282
|
+
repeated :User, :users, 1
|
|
283
|
+
end
|
|
284
|
+
class VoiceTarget < ::Protobuf::Message
|
|
285
|
+
defined_in __FILE__
|
|
286
|
+
class Target < ::Protobuf::Message
|
|
287
|
+
defined_in __FILE__
|
|
288
|
+
repeated :uint32, :session, 1
|
|
289
|
+
optional :uint32, :channel_id, 2
|
|
290
|
+
optional :string, :group, 3
|
|
291
|
+
optional :bool, :links, 4, :default => false
|
|
292
|
+
optional :bool, :children, 5, :default => false
|
|
293
|
+
end
|
|
294
|
+
optional :uint32, :id, 1
|
|
295
|
+
repeated :Target, :targets, 2
|
|
296
|
+
end
|
|
297
|
+
class PermissionQuery < ::Protobuf::Message
|
|
298
|
+
defined_in __FILE__
|
|
299
|
+
optional :uint32, :channel_id, 1
|
|
300
|
+
optional :uint32, :permissions, 2
|
|
301
|
+
optional :bool, :flush, 3, :default => false
|
|
302
|
+
end
|
|
303
|
+
class CodecVersion < ::Protobuf::Message
|
|
304
|
+
defined_in __FILE__
|
|
305
|
+
required :int32, :alpha, 1
|
|
306
|
+
required :int32, :beta, 2
|
|
307
|
+
required :bool, :prefer_alpha, 3, :default => true
|
|
308
|
+
end
|
|
309
|
+
class UserStats < ::Protobuf::Message
|
|
310
|
+
defined_in __FILE__
|
|
311
|
+
class Stats < ::Protobuf::Message
|
|
312
|
+
defined_in __FILE__
|
|
313
|
+
optional :uint32, :good, 1
|
|
314
|
+
optional :uint32, :late, 2
|
|
315
|
+
optional :uint32, :lost, 3
|
|
316
|
+
optional :uint32, :resync, 4
|
|
317
|
+
end
|
|
318
|
+
optional :uint32, :session, 1
|
|
319
|
+
optional :bool, :stats_only, 2, :default => false
|
|
320
|
+
repeated :bytes, :certificates, 3
|
|
321
|
+
optional :Stats, :from_client, 4
|
|
322
|
+
optional :Stats, :from_server, 5
|
|
323
|
+
optional :uint32, :udp_packets, 6
|
|
324
|
+
optional :uint32, :tcp_packets, 7
|
|
325
|
+
optional :float, :udp_ping_avg, 8
|
|
326
|
+
optional :float, :udp_ping_var, 9
|
|
327
|
+
optional :float, :tcp_ping_avg, 10
|
|
328
|
+
optional :float, :tcp_ping_var, 11
|
|
329
|
+
optional :Version, :version, 12
|
|
330
|
+
repeated :int32, :celt_versions, 13
|
|
331
|
+
optional :bytes, :address, 14
|
|
332
|
+
optional :uint32, :bandwidth, 15
|
|
333
|
+
optional :uint32, :onlinesecs, 16
|
|
334
|
+
optional :uint32, :idlesecs, 17
|
|
335
|
+
optional :bool, :strong_certificate, 18, :default => false
|
|
336
|
+
end
|
|
337
|
+
class RequestBlob < ::Protobuf::Message
|
|
338
|
+
defined_in __FILE__
|
|
339
|
+
repeated :uint32, :session_texture, 1
|
|
340
|
+
repeated :uint32, :session_comment, 2
|
|
341
|
+
repeated :uint32, :channel_description, 3
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
package MumbleProto;
|
|
2
|
+
|
|
3
|
+
option optimize_for = SPEED;
|
|
4
|
+
|
|
5
|
+
message Version {
|
|
6
|
+
optional uint32 version = 1;
|
|
7
|
+
optional string release = 2;
|
|
8
|
+
optional string os = 3;
|
|
9
|
+
optional string os_version = 4;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
message UDPTunnel {
|
|
13
|
+
required bytes packet = 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
message Authenticate {
|
|
17
|
+
optional string username = 1;
|
|
18
|
+
optional string password = 2;
|
|
19
|
+
repeated string tokens = 3;
|
|
20
|
+
repeated int32 celt_versions = 4;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
message Ping {
|
|
24
|
+
optional uint64 timestamp = 1;
|
|
25
|
+
optional uint32 good = 2;
|
|
26
|
+
optional uint32 late = 3;
|
|
27
|
+
optional uint32 lost = 4;
|
|
28
|
+
optional uint32 resync = 5;
|
|
29
|
+
optional uint32 udp_packets = 6;
|
|
30
|
+
optional uint32 tcp_packets = 7;
|
|
31
|
+
optional float udp_ping_avg = 8;
|
|
32
|
+
optional float udp_ping_var = 9;
|
|
33
|
+
optional float tcp_ping_avg = 10;
|
|
34
|
+
optional float tcp_ping_var = 11;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
message Reject {
|
|
38
|
+
enum RejectType {
|
|
39
|
+
None = 0;
|
|
40
|
+
WrongVersion = 1;
|
|
41
|
+
InvalidUsername = 2;
|
|
42
|
+
WrongUserPW = 3;
|
|
43
|
+
WrongServerPW = 4;
|
|
44
|
+
UsernameInUse = 5;
|
|
45
|
+
ServerFull = 6;
|
|
46
|
+
NoCertificate = 7;
|
|
47
|
+
}
|
|
48
|
+
optional RejectType type = 1;
|
|
49
|
+
optional string reason = 2;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
message ServerConfig {
|
|
53
|
+
optional uint32 max_bandwidth = 1;
|
|
54
|
+
optional string welcome_text = 2;
|
|
55
|
+
optional bool allow_html = 3;
|
|
56
|
+
optional uint32 message_length = 4;
|
|
57
|
+
optional uint32 image_message_length = 5;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
message ServerSync {
|
|
61
|
+
optional uint32 session = 1;
|
|
62
|
+
optional uint32 max_bandwidth = 2;
|
|
63
|
+
optional string welcome_text = 3;
|
|
64
|
+
optional uint64 permissions = 4;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
message ChannelRemove {
|
|
68
|
+
required uint32 channel_id = 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
message ChannelState {
|
|
72
|
+
optional uint32 channel_id = 1;
|
|
73
|
+
optional uint32 parent = 2;
|
|
74
|
+
optional string name = 3;
|
|
75
|
+
repeated uint32 links = 4;
|
|
76
|
+
optional string description = 5;
|
|
77
|
+
repeated uint32 links_add = 6;
|
|
78
|
+
repeated uint32 links_remove = 7;
|
|
79
|
+
optional bool temporary = 8 [default = false];
|
|
80
|
+
optional int32 position = 9 [default = 0];
|
|
81
|
+
optional bytes description_hash = 10;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
message UserRemove {
|
|
85
|
+
required uint32 session = 1;
|
|
86
|
+
optional uint32 actor = 2;
|
|
87
|
+
optional string reason = 3;
|
|
88
|
+
optional bool ban = 4;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
message UserState {
|
|
92
|
+
optional uint32 session = 1;
|
|
93
|
+
optional uint32 actor = 2;
|
|
94
|
+
optional string name = 3;
|
|
95
|
+
optional uint32 user_id = 4;
|
|
96
|
+
optional uint32 channel_id = 5;
|
|
97
|
+
optional bool mute = 6;
|
|
98
|
+
optional bool deaf = 7;
|
|
99
|
+
optional bool suppress = 8;
|
|
100
|
+
optional bool self_mute = 9;
|
|
101
|
+
optional bool self_deaf = 10;
|
|
102
|
+
optional bytes texture = 11;
|
|
103
|
+
optional bytes plugin_context = 12;
|
|
104
|
+
optional string plugin_identity = 13;
|
|
105
|
+
optional string comment = 14;
|
|
106
|
+
optional string hash = 15;
|
|
107
|
+
optional bytes comment_hash = 16;
|
|
108
|
+
optional bytes texture_hash = 17;
|
|
109
|
+
optional bool priority_speaker = 18;
|
|
110
|
+
optional bool recording = 19;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
message BanList {
|
|
114
|
+
message BanEntry {
|
|
115
|
+
required bytes address = 1;
|
|
116
|
+
required uint32 mask = 2;
|
|
117
|
+
optional string name = 3;
|
|
118
|
+
optional string hash = 4;
|
|
119
|
+
optional string reason = 5;
|
|
120
|
+
optional string start = 6;
|
|
121
|
+
optional uint32 duration = 7;
|
|
122
|
+
}
|
|
123
|
+
repeated BanEntry bans = 1;
|
|
124
|
+
optional bool query = 2 [default = false];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
message TextMessage {
|
|
128
|
+
optional uint32 actor = 1;
|
|
129
|
+
repeated uint32 session = 2;
|
|
130
|
+
repeated uint32 channel_id = 3;
|
|
131
|
+
repeated uint32 tree_id = 4;
|
|
132
|
+
required string message = 5;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
message PermissionDenied {
|
|
136
|
+
enum DenyType {
|
|
137
|
+
Text = 0;
|
|
138
|
+
Permission = 1;
|
|
139
|
+
SuperUser = 2;
|
|
140
|
+
ChannelName = 3;
|
|
141
|
+
TextTooLong = 4;
|
|
142
|
+
H9K = 5;
|
|
143
|
+
TemporaryChannel = 6;
|
|
144
|
+
MissingCertificate = 7;
|
|
145
|
+
UserName = 8;
|
|
146
|
+
ChannelFull = 9;
|
|
147
|
+
}
|
|
148
|
+
optional uint32 permission = 1;
|
|
149
|
+
optional uint32 channel_id = 2;
|
|
150
|
+
optional uint32 session = 3;
|
|
151
|
+
optional string reason = 4;
|
|
152
|
+
optional DenyType type = 5;
|
|
153
|
+
optional string name = 6;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
message ACL {
|
|
157
|
+
message ChanGroup {
|
|
158
|
+
required string name = 1;
|
|
159
|
+
optional bool inherited = 2 [default = true];
|
|
160
|
+
optional bool inherit = 3 [default = true];
|
|
161
|
+
optional bool inheritable = 4 [default = true];
|
|
162
|
+
repeated uint32 add = 5;
|
|
163
|
+
repeated uint32 remove = 6;
|
|
164
|
+
repeated uint32 inherited_members = 7;
|
|
165
|
+
}
|
|
166
|
+
message ChanACL {
|
|
167
|
+
optional bool apply_here = 1 [default = true];
|
|
168
|
+
optional bool apply_subs = 2 [default = true];
|
|
169
|
+
optional bool inherited = 3 [default = true];
|
|
170
|
+
optional uint32 user_id = 4;
|
|
171
|
+
optional string group = 5;
|
|
172
|
+
optional uint32 grant = 6;
|
|
173
|
+
optional uint32 deny = 7;
|
|
174
|
+
}
|
|
175
|
+
required uint32 channel_id = 1;
|
|
176
|
+
optional bool inherit_acls = 2 [default = true];
|
|
177
|
+
repeated ChanGroup groups = 3;
|
|
178
|
+
repeated ChanACL acls = 4;
|
|
179
|
+
optional bool query = 5 [default = false];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
message QueryUsers {
|
|
183
|
+
repeated uint32 ids = 1;
|
|
184
|
+
repeated string names = 2;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
message CryptSetup {
|
|
188
|
+
optional bytes key = 1;
|
|
189
|
+
optional bytes client_nonce = 2;
|
|
190
|
+
optional bytes server_nonce = 3;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
message ContextActionAdd {
|
|
194
|
+
enum Context {
|
|
195
|
+
Server = 0x01;
|
|
196
|
+
Channel = 0x02;
|
|
197
|
+
User = 0x04;
|
|
198
|
+
}
|
|
199
|
+
required string action = 1;
|
|
200
|
+
required string text = 2;
|
|
201
|
+
optional uint32 context = 3;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
message ContextAction {
|
|
205
|
+
optional uint32 session = 1;
|
|
206
|
+
optional uint32 channel_id = 2;
|
|
207
|
+
required string action = 3;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
message UserList {
|
|
211
|
+
message User {
|
|
212
|
+
required uint32 user_id = 1;
|
|
213
|
+
optional string name = 2;
|
|
214
|
+
}
|
|
215
|
+
repeated User users = 1;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
message VoiceTarget {
|
|
219
|
+
message Target {
|
|
220
|
+
repeated uint32 session = 1;
|
|
221
|
+
optional uint32 channel_id = 2;
|
|
222
|
+
optional string group = 3;
|
|
223
|
+
optional bool links = 4 [default = false];
|
|
224
|
+
optional bool children = 5 [default = false];
|
|
225
|
+
}
|
|
226
|
+
optional uint32 id = 1;
|
|
227
|
+
repeated Target targets = 2;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
message PermissionQuery {
|
|
231
|
+
optional uint32 channel_id = 1;
|
|
232
|
+
optional uint32 permissions = 2;
|
|
233
|
+
optional bool flush = 3 [default = false];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
message CodecVersion {
|
|
237
|
+
required int32 alpha = 1;
|
|
238
|
+
required int32 beta = 2;
|
|
239
|
+
required bool prefer_alpha = 3 [default = true];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
message UserStats {
|
|
243
|
+
message Stats {
|
|
244
|
+
optional uint32 good = 1;
|
|
245
|
+
optional uint32 late = 2;
|
|
246
|
+
optional uint32 lost = 3;
|
|
247
|
+
optional uint32 resync = 4;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
optional uint32 session = 1;
|
|
251
|
+
optional bool stats_only = 2 [default = false];
|
|
252
|
+
repeated bytes certificates = 3;
|
|
253
|
+
optional Stats from_client = 4;
|
|
254
|
+
optional Stats from_server = 5;
|
|
255
|
+
|
|
256
|
+
optional uint32 udp_packets = 6;
|
|
257
|
+
optional uint32 tcp_packets = 7;
|
|
258
|
+
optional float udp_ping_avg = 8;
|
|
259
|
+
optional float udp_ping_var = 9;
|
|
260
|
+
optional float tcp_ping_avg = 10;
|
|
261
|
+
optional float tcp_ping_var = 11;
|
|
262
|
+
|
|
263
|
+
optional Version version = 12;
|
|
264
|
+
repeated int32 celt_versions = 13;
|
|
265
|
+
optional bytes address = 14;
|
|
266
|
+
optional uint32 bandwidth = 15;
|
|
267
|
+
optional uint32 onlinesecs = 16;
|
|
268
|
+
optional uint32 idlesecs = 17;
|
|
269
|
+
optional bool strong_certificate = 18 [default = false];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
message RequestBlob {
|
|
273
|
+
repeated uint32 session_texture = 1;
|
|
274
|
+
repeated uint32 session_comment = 2;
|
|
275
|
+
repeated uint32 channel_description = 3;
|
|
276
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module Mumble
|
|
2
|
+
class PacketDataStream
|
|
3
|
+
def initialize(data=nil)
|
|
4
|
+
@data = data || 0.chr * 1024
|
|
5
|
+
@data = @data.split ''
|
|
6
|
+
@pos = 0
|
|
7
|
+
@ok = true
|
|
8
|
+
@capacity = @data.size
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def valid
|
|
12
|
+
@ok
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def size
|
|
16
|
+
@pos
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def left
|
|
20
|
+
@capacity - @pos
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def append(val)
|
|
24
|
+
if @pos < @capacity
|
|
25
|
+
@data[@pos] = val.chr
|
|
26
|
+
skip
|
|
27
|
+
else
|
|
28
|
+
@ok = false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def append_block(data)
|
|
33
|
+
len = data.size
|
|
34
|
+
if len < left
|
|
35
|
+
@data[@pos, len] = data.split('')
|
|
36
|
+
skip len
|
|
37
|
+
else
|
|
38
|
+
@ok = false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_block(len)
|
|
43
|
+
if len < left
|
|
44
|
+
ret = @data[@pos, len]
|
|
45
|
+
skip len
|
|
46
|
+
else
|
|
47
|
+
@ok = false
|
|
48
|
+
ret = []
|
|
49
|
+
end
|
|
50
|
+
ret
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def get_next
|
|
54
|
+
if @pos < @capacity
|
|
55
|
+
ret = @data[@pos].ord
|
|
56
|
+
skip
|
|
57
|
+
else
|
|
58
|
+
ret = 0
|
|
59
|
+
@ok = false
|
|
60
|
+
end
|
|
61
|
+
ret
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def rewind
|
|
65
|
+
@pos = 0
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def skip(len=1)
|
|
69
|
+
len < left ? @pos += len : @ok = false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def put_int(int)
|
|
73
|
+
if !(int & 0x8000000000000000).zero? && (~int < 0x100000000)
|
|
74
|
+
int = ~int
|
|
75
|
+
puts int
|
|
76
|
+
if int <= 0x3
|
|
77
|
+
# Shortcase for -1 to -4
|
|
78
|
+
append(0xFC | int)
|
|
79
|
+
else
|
|
80
|
+
append(0xF8)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if int < 0x80
|
|
85
|
+
# Need top bit clear
|
|
86
|
+
append(int)
|
|
87
|
+
elsif int < 0x4000
|
|
88
|
+
# Need top two bits clear
|
|
89
|
+
append((int >> 8) | 0x80)
|
|
90
|
+
append(int & 0xFF)
|
|
91
|
+
elsif int < 0x200000
|
|
92
|
+
# Need top three bits clear
|
|
93
|
+
append((int >> 16) | 0xC0)
|
|
94
|
+
append((int >> 8) & 0xFF)
|
|
95
|
+
append(int & 0xFF)
|
|
96
|
+
elsif int < 0x10000000
|
|
97
|
+
# Need top four bits clear
|
|
98
|
+
append((int >> 24) | 0xE0)
|
|
99
|
+
append((int >> 16) & 0xFF)
|
|
100
|
+
append((int >> 8) & 0xFF)
|
|
101
|
+
append(int & 0xFF)
|
|
102
|
+
elsif int < 0x100000000
|
|
103
|
+
# It's a full 32-bit integer.
|
|
104
|
+
append(0xF0)
|
|
105
|
+
append((int >> 24) & 0xFF)
|
|
106
|
+
append((int >> 16) & 0xFF)
|
|
107
|
+
append((int >> 8) & 0xFF)
|
|
108
|
+
append(int & 0xFF)
|
|
109
|
+
else
|
|
110
|
+
# It's a 64-bit value.
|
|
111
|
+
append(0xF4)
|
|
112
|
+
append((int >> 56) & 0xFF)
|
|
113
|
+
append((int >> 48) & 0xFF)
|
|
114
|
+
append((int >> 40) & 0xFF)
|
|
115
|
+
append((int >> 32) & 0xFF)
|
|
116
|
+
append((int >> 24) & 0xFF)
|
|
117
|
+
append((int >> 16) & 0xFF)
|
|
118
|
+
append((int >> 8) & 0xFF)
|
|
119
|
+
append(int & 0xFF)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def get_int
|
|
124
|
+
v = get_next
|
|
125
|
+
int = 0
|
|
126
|
+
|
|
127
|
+
if (v & 0x80) == 0x00
|
|
128
|
+
int = v & 0x7F
|
|
129
|
+
elsif (v & 0xC0) == 0x80
|
|
130
|
+
int = (v & 0x3F) << 8 | get_next
|
|
131
|
+
elsif (v & 0xF0) == 0xF0
|
|
132
|
+
x = v & 0xFC
|
|
133
|
+
if x == 0xF0
|
|
134
|
+
int = get_next << 24 | get_next << 16 | get_next << 8 | get_next
|
|
135
|
+
elsif x == 0xF4
|
|
136
|
+
int = get_next << 56 | get_next << 48 | get_next << 40 | get_next << 32 |
|
|
137
|
+
get_next << 24 | get_next << 16 | get_next << 8 | get_next
|
|
138
|
+
elsif x == 0xF8
|
|
139
|
+
int = get_int
|
|
140
|
+
int = ~int
|
|
141
|
+
elsif x == 0xFC
|
|
142
|
+
int = v & 0x03
|
|
143
|
+
int = ~int
|
|
144
|
+
else
|
|
145
|
+
@ok = false
|
|
146
|
+
int = 0
|
|
147
|
+
end
|
|
148
|
+
elsif (v & 0xF0) == 0xE0
|
|
149
|
+
int = (v & 0x0F) << 24 | get_next << 16 | get_next << 8 | get_next
|
|
150
|
+
elsif (v & 0xE0) == 0xC0
|
|
151
|
+
int = (v & 0x1F) << 16 | get_next << 8 | get_next
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return int
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
data/mumble-ruby.gemspec
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.expand_path('../lib/mumble-ruby/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |gem|
|
|
5
|
+
gem.authors = ["Matthew Perry"]
|
|
6
|
+
gem.email = ["perrym5@rpi.edu"]
|
|
7
|
+
gem.description = %q{Ruby API for interacting with a mumble server}
|
|
8
|
+
gem.summary = %q{Implements the mumble VOIP protocol in ruby for more easily writing clients.}
|
|
9
|
+
gem.homepage = "http://www.github.com/perrym5/mumble-ruby"
|
|
10
|
+
|
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
14
|
+
gem.name = "mumble-ruby"
|
|
15
|
+
gem.require_paths = ["lib"]
|
|
16
|
+
gem.version = Mumble::VERSION
|
|
17
|
+
|
|
18
|
+
gem.add_dependency "activesupport"
|
|
19
|
+
gem.add_dependency "celt-ruby"
|
|
20
|
+
|
|
21
|
+
gem.add_development_dependency "ruby_protobuf"
|
|
22
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mumble-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Matthew Perry
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-04-26 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: activesupport
|
|
16
|
+
requirement: &11094520 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *11094520
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: celt-ruby
|
|
27
|
+
requirement: &11093120 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ! '>='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *11093120
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: ruby_protobuf
|
|
38
|
+
requirement: &11091560 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ! '>='
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
type: :development
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *11091560
|
|
47
|
+
description: Ruby API for interacting with a mumble server
|
|
48
|
+
email:
|
|
49
|
+
- perrym5@rpi.edu
|
|
50
|
+
executables: []
|
|
51
|
+
extensions: []
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- .gitignore
|
|
55
|
+
- .rvmrc
|
|
56
|
+
- Gemfile
|
|
57
|
+
- README.rdoc
|
|
58
|
+
- Rakefile
|
|
59
|
+
- lib/mumble-ruby.rb
|
|
60
|
+
- lib/mumble-ruby/audio_stream.rb
|
|
61
|
+
- lib/mumble-ruby/client.rb
|
|
62
|
+
- lib/mumble-ruby/connection.rb
|
|
63
|
+
- lib/mumble-ruby/messages.rb
|
|
64
|
+
- lib/mumble-ruby/mumble.proto
|
|
65
|
+
- lib/mumble-ruby/packet_data_stream.rb
|
|
66
|
+
- lib/mumble-ruby/version.rb
|
|
67
|
+
- mumble-ruby.gemspec
|
|
68
|
+
homepage: http://www.github.com/perrym5/mumble-ruby
|
|
69
|
+
licenses: []
|
|
70
|
+
post_install_message:
|
|
71
|
+
rdoc_options: []
|
|
72
|
+
require_paths:
|
|
73
|
+
- lib
|
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
|
+
none: false
|
|
76
|
+
requirements:
|
|
77
|
+
- - ! '>='
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '0'
|
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
|
+
none: false
|
|
82
|
+
requirements:
|
|
83
|
+
- - ! '>='
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '0'
|
|
86
|
+
requirements: []
|
|
87
|
+
rubyforge_project:
|
|
88
|
+
rubygems_version: 1.8.10
|
|
89
|
+
signing_key:
|
|
90
|
+
specification_version: 3
|
|
91
|
+
summary: Implements the mumble VOIP protocol in ruby for more easily writing clients.
|
|
92
|
+
test_files: []
|