Annotation of MumbleDicebot/mumble_protocol.py, Revision 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