Annotation of MumbleDicebot/mumble_protocol.py, Revision 1.1.1.1
1.1 rubenllo 1: from twisted.internet import protocol, task
2: from utils import parse_varint
3: import mumble_protobuf
4: import struct
5:
6: MUMBLE_VERSION = 66052 # 1.2.4
7: LOGGING = True
8:
9:
10: # Packet types
11: class Packet:
12: VERSION = 0
13: UDPTUNNEL = 1
14: AUTHENTICATE = 2
15: PING = 3
16: USERSTATE = 9
17: TEXTMESSAGE = 11
18:
19: PACKET_NAMES = {
20: 0: "Version",
21: 1: "UDPTunnel",
22: 2: "Authenticate",
23: 3: "Ping",
24: 4: "Reject",
25: 5: "ServerSync",
26: 6: "ChannelRemove",
27: 7: "ChannelState",
28: 8: "UserRemove",
29: 9: "UserState",
30: 10: "BanList",
31: 11: "TextMessage",
32: 12: "PermissionDenied",
33: 13: "ACL",
34: 14: "QueryUsers",
35: 15: "CryptSetup",
36: 16: "ContextActionModify",
37: 17: "ContextAction",
38: 18: "UserList",
39: 19: "VoiceTarget",
40: 20: "PermissionQuery",
41: 21: "CodecVersion",
42: 22: "UserStats",
43: 23: "RequestBlob",
44: 24: "ServerConfig",
45: 25: "SuggestConfig"
46: }
47:
48:
49: class VoicePacket():
50: def __init__(self):
51: self.session = 0
52: self.type = 0
53: self.target = 0
54: self.sequence = 0
55: self.lengths = []
56: self.frames = []
57:
58: def __str__(self):
59: s = "Session: {}\nType: {}\nTarget: {}\nSequence: {}\n"
60: return s.format(self.session, self.type, self.target, self.sequence)
61:
62:
63: class MumbleClient(protocol.Protocol):
64: """Implements mumble's protocol for communicating with a murmur server.
65: http://mumble.sourceforge.net/Protocol"""
66:
67: def connectionMade(self):
68: # Send version packet
69: self.send_version()
70:
71: # Sends authentication packet
72: self.send_auth()
73:
74: # Starts sending heartbeat
75: self.heartbeat = task.LoopingCall(self.send_ping)
76: self.heartbeat.start(10, now=False)
77:
78: def dataReceived(self, data):
79: while data:
80: p_type, parsed, data = self.unpack_data(data)
81:
82: if LOGGING and p_type not in [1, 3, 7]:
83: print "Type:", parsed.DESCRIPTOR.name
84: print parsed
85:
86: if p_type in self.get_handlers().keys():
87: self.get_handlers()[p_type](parsed)
88:
89: def connectionLost(self, reason):
90: print "Connection lost"
91:
92: # Generates the mumble protocol packets
93: def pack_data(self, packet_type, payload):
94: return struct.pack(">hi%ds" % len(payload), packet_type, len(payload), payload)
95:
96: def send_version(self):
97: client_version = mumble_protobuf.Version()
98: client_version.version = MUMBLE_VERSION
99: payload = client_version.SerializeToString()
100: packet = self.pack_data(Packet.VERSION, payload)
101: self.transport.write(packet)
102:
103: def send_auth(self):
104: client_auth = mumble_protobuf.Authenticate()
105: client_auth.username = self.factory.nickname
106: client_auth.password = self.factory.password
107: client_auth.celt_versions.append(-2147483637)
108: client_auth.celt_versions.append(-2147483632)
109: client_auth.opus = True
110: payload = client_auth.SerializeToString()
111: packet = self.pack_data(Packet.AUTHENTICATE, payload)
112: self.transport.write(packet)
113:
114: def send_ping(self):
115: ping = mumble_protobuf.Ping()
116: payload = ping.SerializeToString()
117: packet = self.pack_data(Packet.PING, payload)
118: self.transport.write(packet)
119:
120: def send_textmessage(self, text, channels=[], users=[]):
121: """Send chat message to a list of channels or users by ids"""
122: msg = mumble_protobuf.TextMessage()
123: msg.message = text
124: if channels:
125: msg.channel_id.extend(channels)
126: if users:
127: msg.session.extend(users)
128: payload = msg.SerializeToString()
129: packet = self.pack_data(Packet.TEXTMESSAGE, payload)
130: self.transport.write(packet)
131:
132: def move_user(self, channel_id, user_id):
133: state = mumble_protobuf.UserState()
134: state.session = user_id
135: state.channel_id = channel_id
136: payload = state.SerializeToString()
137: packet = self.pack_data(Packet.USERSTATE, payload)
138: self.transport.write(packet)
139:
140: def mute_or_deaf(self, user_id, mute=True, deaf=True):
141: state = mumble_protobuf.UserState()
142: state.session = user_id
143: if user_id == self.session:
144: state.self_mute = mute
145: state.self_deaf = deaf
146: else:
147: state.mute = mute
148: state.deaf = deaf
149: payload = state.SerializeToString()
150: packet = self.pack_data(Packet.USERSTATE, payload)
151: self.transport.write(packet)
152:
153: # Parse mumble protocol packets
154: def unpack_data(self, data):
155: p_type, p_length = struct.unpack(">hi", data[:6])
156: p_data = data[6:p_length+6]
157: parsed = getattr(mumble_protobuf, PACKET_NAMES[p_type])()
158:
159: if p_type == Packet.UDPTUNNEL:
160: parsed = self.parse_voicedata(p_data)
161: else:
162: parsed.ParseFromString(p_data)
163:
164: return p_type, parsed, data[p_length+6:]
165:
166: def parse_voicedata(self, data):
167: p = VoicePacket()
168: header, = struct.unpack(">B", data[0])
169: p.type, p.target = header >> 5, header & 31
170: p.session, data = parse_varint(data[1:])
171: p.sequence, data = parse_varint(data)
172:
173: # Swap to enable saving voice packets
174: # while data:
175: while False:
176: audio, = struct.unpack(">B", data[0])
177: a_terminator, a_length = audio >> 7, audio & 127
178: audio_data, data = data[1:a_length+1], data[a_length+1:]
179: p.frames.append(audio_data)
180: p.lengths.append(a_length)
181: if a_terminator == 0:
182: break
183:
184: return p
185:
186: # Handles various interactions
187: def get_handlers(self):
188: handlers = {
189: 1: self.handle_udptunnel,
190: 7: self.handle_channelstate,
191: 8: self.handle_userremove,
192: 9: self.handle_userstate,
193: 11: self.handle_textmessage
194: }
195: return handlers
196:
197: def handle_udptunnel(self, p):
198: pass
199:
200: def handle_channelstate(self, p):
201: pass
202:
203: def handle_userremove(self, p):
204: pass
205:
206: def handle_userstate(self, p):
207: pass
208:
209: def handle_textmessage(self, p):
210: pass
CVSweb