#! /usr/bin/env python # pymuseekd - Python tools for museekd # # Copyright (C) 2003-2004 Hyriand # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA VERSION = "0.1.0" import struct, socket, sys, time, urllib, pwd, os, select from time import sleep import messages, driver museekdaemon = "localhost:2250" password = "" def output(s): print s sys.stdout.flush() def search(s, q): s.put(museekdsearch(q)) code, data = s.get() m = museekdsearch(s).parse(data) output("got our ticket, %i\n" % m.ticket) process(ticket = m.ticket) def print_transfers(transfers): for tr in transfers: if tr[2] == 11: state = tr[3] else: state = museekdhelp.states[tr[2]] print "%s\t%s\t%s\t%i\t%i\t%i" % (tr[0], tr[1], state, tr[4], tr[5], tr[6]) print def process(ticket = None, room = None, userinfo = None, usershares = None, monitor = 0): while 1: try: r, w, e = select([s], [], [s], 1.0) if e: output("connection closed") break if not r: continue code, data = s.get() if code == 2: m = museekdserverstate().parse(data) if not m.state: print "disconnected from server" else: print "logged in as %s" % m.username elif code == 3: m = museekdroomstate().parse(data) print "rooms:" for r in m.roomlist.keys(): print r for r in m.joined_rooms.keys(): print "%s\t%i" % (r, m.joined[r]) elif ticket is not None and code == 5: m = museekdsearchreply().parse(data) for r in m.results: if r[2] == "mp3" and len(r[3]) >= 3: d = "%i\t%i\t%i" % (r[3][0], r[3][1], r[3][2]) else: d = "\t\t" url = "slsk://" + urllib.pathname2url("%s/%s" % (m.user, r[0].replace("\\", "/"))) output("%s\t%i\t%i\t%s\t%i\t%s" % (url, r[1], m.speed, m.free and "Y" or "N", m.queue, d)) elif room is not None and code == 6: m = museekdsaychatroom().parse(data) if room != "" and m.room != room: continue if m.line[:4] == "/me ": msg = "* %s %s" % (m.user, m.line[4:]) else: msg = "%s: %s" % (m.user, m.line) output("%s [%s] %s" % (time.strftime("%H:%M"), m.room, msg)) elif room is not None and code == 7: m = museekdjoinroom().parse(data) if room != "" and m.room != room: continue output("-- joined room %s (%i users)" % (m.room, len(m.users))) elif room is not None and code == 8: m = museekdleaveroom().parse(data) if room != "" and m.room != room: continue output("-- left room %s" % m.room) elif room is not None and code == 9: m = museekduserjoinedroom().parse(data) if room != "" and m.room != room: continue output("-- user %s joined room %s" % (m.user, m.room)) elif room is not None and code == 10: m = museekduserleftroom().parse(data) if room != "" and m.room != room: continue output("-- user %s left room %s" % (m.user, m.room)) elif code == 11: m = museekdprivatemessage().parse(data) if m.message[:4] == "/me ": msg = "* %s %s" % (m.user, m.message[4:]) else: msg = "%s: %s" % (m.user, m.message) output("%s [%s] %s" % (time.strftime("%H:%M", time.localtime(m.timestamp)), m.user, msg)) elif userinfo is not None and code == 12: m = museekduserinfo().parse(data) if userinfo != "" and m.user != userinfo: continue output("Userinfo for user %s:\nDescription:" % m.user) output(m.info) output("Uploads: %i, queue length: %i, slots free: %s" % (m.uploads, m.queue, m.slotsfree and "Y" or "N")) if m.picture: f = open("%s.img" % m.user, "w") f.write(m.picture) f.close() output("") if userinfo != "": sys.exit() elif usershares is not None and code == 13: m = museekdusershares().parse(data) if usershares != "" and m.user != usershares: continue output("Shared file list for user %s:" % m.user) dirs = m.shares.keys() dirs.sort() for dir in dirs: output(dir) files = m.shares[dir].keys() files.sort() for file in files: data = m.shares[dir][file] if data[1] == "mp3" and len(data[2]) == 3: d = "%i\t%i\t%i" % (data[2][0], data[2][1], data[2][2]) else: d = "\t\t" output("\t%s\t%i\t%s" % (file, data[0], d)) output("") if usershares != "": sys.exit() elif code == 14: m = museekdtransferstate().parse(data) downloads = m.downloads uploads = m.uploads print "Downloads:" print_transfers(downloads) print "Uploads:" print_transfers(uploads) if not monitor: sys.exit(0) elif code == 15: m = museekdtransferupdate().parse(data) if m.upload: l = uploads else: l = downloads for i in l: if i[0] == m.transfer[0] and i[1] == m.transfer[1]: i[:] = m.transfer[:] break else: l.append(m.transfer) print "Downloads:" print_transfers(downloads) print "Uploads:" print_transfers(uploads) except KeyboardInterrupt: break def get_room_state(s): code, data = s.get() if code != 3: output("invalid message type received..") sys.exit() return museekdroomstate().parse(data) def help(): output(""" museekdctl %s -- commandline control utility for museekd syntax: museekdctl SERVER SERVER: :host:port:password -- connect using TCP/IP :/path/to/sock:password -- connect to unix socket commands: search -- search for and show results tab separated rooms -- show list of rooms joined -- show list of joined rooms info [room] -- show info about room (all rooms if ommitted) stalk [room] -- show chat log for room (all rooms if ommitted) tickers [room] -- show tickers in room (all rooms if ommitted) say -- say in join -- join leave -- leave messages -- monitor private messages pm -- send to privately browse [user] -- shows users shares (monitor shares if user ommited) info [user] -- shows users info (monitor info if user ommited) (dumps image to .img if picture is available) transfers -- dump the up- and download queue transfermon -- monitor transfers get slsk://... -- add file to the download queue """ % VERSION) class museekcontrol(driver.Driver): def __init__(self): driver.Driver.__init__(self) self.s_query = {} self.search_number = 0 self.count = 0 self.login = 0 def disconnect(self): driver.Driver.close(self) def connect(self): try: driver.Driver.connect(self, museekdaemon, password, messages.EM_CHAT | messages.EM_USERINFO| messages.EM_PRIVATE| messages.EM_TRANSFERS | messages.EM_USERSHARES | messages.EM_CONFIG) except Exception, e: print e def process(self): d = 0 r, w, x = select.select([self.socket, sys.stdin], [], [self.socket], d) if self.socket in r: driver.Driver.process(self) if self.login == 1: if want == "say": self.send(messages.SayRoom(room, message)) sys.exit() elif want == "join": self.send(messages.JoinRoom(room)) sys.exit() elif want in ("leave", "part"): self.send(messages.LeaveRoom(room)) sys.exit() elif want == "messages": pass elif want == "stalk": pass elif want == "rooms": pass elif want == "joined": pass elif want in ("pm", "private"): self.send(messages.PrivateMessage(1, user, message)) print "Sent \"", message, "\" to", user sys.exit() elif want == "away": pass elif want == "info": if user != None: self.send(messages.UserInfo(user)) elif want == "ip": if user != None: self.send(messages.PeerAddress(user)) elif want == "stats": if self.count == 0: if user != None: self.send(messages.PeerStats(user)) self.count += 1 elif want == "browse": if self.count == 0: if user != None: self.send(messages.UserShares(user)) self.count += 1 elif want == "ticker": if room != None: self.send(messages.RoomTickerSet(room, message)) sys.exit() elif want == "buddy": if reason != None: self.send(messages.ConfigSet("buddies", user, reason)) else: self.send(messages.ConfigSet("buddies", user, "")) sys.exit() elif want == "ban": #if reason != None: # self.send(messages.ConfigSet("banned", user, reason)) #else: if user != None: self.send(messages.ConfigSet("banned", user, "a")) sys.exit() elif want == "ignore": if reason != None: self.send(messages.ConfigSet("ignored", user, reason)) else: self.send(messages.ConfigSet("ignored", user, "")) sys.exit() elif want == "roomlist": self.send(messages.RoomList()) sys.exit() elif want == "download": if user != '': self.send(messages.DownloadFile(user, file)) sys.exit() elif want == "downfolder": if user != '': s = file[:-1] self.send(messages.GetFolderContents(user, file)) sys.exit() elif want == "gsearch": if self.count == 0: self.send(messages.Search(0, query)) self.count += 1 elif want == "bsearch": if self.count == 0: self.send(messages.Search(1, query)) self.count += 1 elif want == "abortdown": self.send(messages.TransferAbort(0, username, path)) #d sys.exit() elif want == "abortup": self.send(messages.TransferAbort(1, username, path)) #u sys.exit() elif want == "removedown": self.send(messages.TransferRemove(0, username, path)) sys.exit() elif want == "removeup": self.send(messages.TransferRemove(1, username, path)) sys.exit() elif want == "transfers": pass elif want == "transfermon": pass sleep(0.001) #print self.count def cb_room_state(self, roomlist, joined, tickers): self.login = 1 if want == "rooms": x = roomlist.keys() x.sort() print "Current rooms in the roomlist:" print "-Size-\t-Name-" for s in x: print "%s\t%s" % (roomlist[s], s) sys.exit() elif want == "roominfo": x = joined.keys() x.sort() if room != None: print "-Size-\t-Room-" print "%s\t%s" % (roomlist[room], room) #joined[s], print "Status\tSpeed\tDown\tFiles\tSlots\tNick" r = joined[room] for u in joined[room]: z = str(r[u][0]) + "\t" + str(r[u][1]) + "\t" + str(r[u][2]) + "\t" + str(r[u][3]) + "\t" + str(r[u][4]) print "%s\t%s" % (z, u) print "" else: for s in x: print "-Size-\t-Room-" print "%s\t%s" % (roomlist[s], s) #joined[s], print "Status\tSpeed\tDown\tFiles\tSlots\tNick" r = joined[s] for u in joined[s]: z = str(r[u][0]) + "\t" + str(r[u][1]) + "\t" + str(r[u][2]) + "\t" + str(r[u][3]) + "\t" + str(r[u][4]) print "%s\t%s" % (z, u) print "" sys.exit() elif want == "joined": x = joined.keys() x.sort() print "You are in the following rooms:" print "-Size-\t-Name-" for s in x: print "%s\t%s" % (roomlist[s], s) #joined[s], sys.exit() elif want == "tickers": x = tickers.keys() x.sort() for s in x: if tickers[s] != {}: print "Room:", s for t in tickers[s]: print "[%s]\t%s" % (t, tickers[s][t]) print "" sys.exit() def cb_server_status_set(self, status): if want == "away": if status == 0: self.send(messages.SetStatus(1)) else: self.send(messages.SetStatus(0)) sys.exit() def cb_room_said(self, roomname, user, text): good =0 if want == "stalk": if room != None: if room == roomname: good = 1 else: good =1 if good ==1: if text[:4] == "/me ": message = "* "+user+" " + text[4:] else: message = "["+user+"] " + text print "%s (%s) %s" % (time.strftime("%b %d %H:%M:%S"), roomname, message) def cb_private_message(self, direction, timestamp, user, message): if want == "messages": if direction == 0: print "PM", time.strftime("%b %d %H:%M:%S"), user+" said: "+ message elif direction == 1: print "PM", time.strftime("%b %d %H:%M:%S"), "You told", user +": "+ message def cb_user_info(self, username, info, picture, uploads, queue, slotsfree): if want == "info": if user != None: if user == username: print "User:", username i = info.split('\n') for line in i: print line print "Queue:", queue, "Uploads:", uploads, "Slots:", slotsfree if picture != '': r = file(str(username)+".image", 'w') print >> r, str(picture) r.close() print "Saved User Image as: %s.image" % username sys.exit() else: print "User:", username i = info.split('\n') for line in i: print line print "Queue:", queue, "Uploads:", uploads, "Slots:", slotsfree if picture != '': r = file(str(username)+".image", 'w') print >> r, str(picture) r.close() print "Saved User Image as: %s.image" % username print "- - - - - - - - - - - - - - - -" def display_shares(self, username, shares): self.browse_number = 0 for dirs, files in shares.items(): result_list = [] if files != {}: output("------\nDIR: " + dirs) for file, stats in files.items(): self.browse_number = self.browse_number +1 size= str(stats[0]/1024)+"KB" ftype =stats[1] if ftype == '': ftype = "None" length = "00:00" bitrate = 'None' else: bitrate =str(stats[2][0]) if bitrate == '': bitrate = 'None' length =str(stats[2][1]) if length != '' and length != None: minutes = int(length)/60 seconds = str( int(length) - (60 * minutes)) if len(seconds) < 2: seconds = '0' + seconds length = str(minutes)+":"+str(seconds) else: length = "00:00" filename = dirs + "\\" + file result_list = user, filename # Activate Number for Result output('['+str(self.browse_number)+'] ' + " Size: " + str(size) + " Type: " + str(ftype) + " Length: " + str(length) + " Bitrate: " + str(bitrate) + "\nFile: " + str(file)) output("Finished browsing: " + username) def cb_user_shares(self, username, shares): if want == "browse": if user != None: if user == username: print "User:", username self.display_shares(username, shares) sys.exit() else: print "User:", username self.display_shares(username, shares) def cb_search_ticket(self, query, ticket): self.s_query[ticket] = query def cb_search_results(self, ticket, user, free, speed, queue, results): # search results if want in ("gsearch", "bsearch"): output("---------\nSearch: " +str(self.s_query[ticket]) + " Results from: User: "+ user ) for result in results: result_list = [] # Create Result List for future use # clear it next interation result_list = ticket, user, free, speed, queue, result[0], result[1], result[2], result[3] # Count Search Result self.search_number += 1 # Display Search Result path = result[0] size = str(result[1]/1024)+'KB' ftype = result[2] if ftype in ('mp3', 'ogg'): if result[3] != []: bitrate = result[3][0] length = result[3][1] minutes = int(length)/60 seconds = str(length - (60 * minutes)) if len(seconds) < 2: seconds = '0' + seconds else: bitrate = 'None' minutes = '00' seconds = '00' length = 0 else: bitrate = 'None' minutes = '00' seconds = '00' length = 0 if free: free = 'Y' else: free = 'N' output("Num: [" +str(self.search_number)+"] Path: "+ path) output(" Size: "+str(size)+ " Bitrate: "+ str(bitrate) + " Length: " + str(minutes)+":"+seconds +" Queue: "+str(queue)+" Speed: "+str(speed) +" Free: " +free) output(" ") sys.exit() def cb_peer_address(self, username, ip, port): if want == "ip": if user != None: if user == username: print "%s's IP: %s Port: %s" % (user, str(ip), str(port)) sys.exit() def cb_peer_stats(self, username, avgspeed, numdownloads, numfiles, numdirs): if want == "stats": if user == username: output("Peer Stats for: %s \nSpeed: %s \tDownloads: %s \nFiles: %s \tDirectories: %s" % (user, avgspeed, numdownloads, numfiles, numdirs)) output("") sys.exit() def cb_disconnected(self): print "--- Disconnected from the Museek Daemon ---" sys.exit() def cb_login_error(self, reason): if reason == "INVPASS": self.invalidpass = 1 print "couldn't log in: Invalid Password" self.connect() else: self.invalidpass = 0 print "couldn't log in: " + reason def cb_login_ok(self): self.invalidpass = 0 print "Logging in..." def cb_server_state(self, state, username): if state: output("Connected to server, username: " + username) else: output("Not connected to server") def cb_transfer_state(self, downloads, uploads): if want in ("transfermon", "transfers"): for transfer in uploads: print "Upload: [%s] Size: %s Pos: %s Rate: %s State: %s %s\nPath: %s " % (transfer.user, transfer.filesize, transfer.filepos, transfer.rate, states[int(transfer.state)], transfer.error, transfer.path) print "- - - - - - - - - - - - - - - -" for transfer in downloads: print "Download: [%s] Size: %s Pos: %s Rate: %s State: %s %s\nPath: %s " % (transfer.user, transfer.filesize, transfer.filepos, transfer.rate, states[int(transfer.state)], transfer.error, transfer.path) print "- - - - - - - - - - - - - - - -" if want == "transfers": sys.exit() def cb_transfer_update(self, transfer): if want == "transfermon": if transfer.is_upload: print "Upload: [%s] Size: %s Pos: %s Rate: %s State: %s %s\nPath: %s " % (transfer.user, transfer.filesize, transfer.filepos, transfer.rate, states[int(transfer.state)], transfer.error, transfer.path) print "- - - - - - - - - - - - - - - -" else: print "Download: [%s] Size: %s Pos: %s Rate: %s State: %s %s\nPath: %s " % (transfer.user, transfer.filesize, transfer.filepos, transfer.rate, states[int(transfer.state)], transfer.error, transfer.path) c = museekcontrol() def start(): try: while 1: if c.socket is None: c.connect() c.process() except Exception, e: print e if len(sys.argv) == 1: help() sys.exit() #sys.argv = [sys.argv[0]] + sys.argv[2:] if len(sys.argv) == 1: output("nothing to do, exiting") sys.exit() if sys.argv[1] in ( "gsearch", "bsearch"): if len(sys.argv) > 2: query = " ".join(sys.argv[2:]).strip() else: query = None want=sys.argv[1] if query: start() else: output("nothing to search for, exiting") if sys.argv[1] in ( "ban", "unban", "buddy", "unbuddy", "ignore", "unignore"): if len(sys.argv) > 2: user = sys.argv[2] else: user = None #if len(sys.argv) > 3: reason = None want=sys.argv[1] start() elif sys.argv[1] == "rooms": want="rooms" start() elif sys.argv[1] == "away": want="away" start() elif sys.argv[1] == "tickers": want="tickers" start() elif sys.argv[1] == "joined": want="joined" start() elif sys.argv[1] == "roomlist": want="roomlist" start() elif sys.argv[1] == "roominfo": want="roominfo" if len(sys.argv) > 2: room = " ".join(sys.argv[2:]).strip() else: room = None start() elif sys.argv[1] == "stalk": if len(sys.argv) > 2: room = " ".join(sys.argv[2:]).strip() else: room = None want="stalk" start() elif sys.argv[1] == "say": if len(sys.argv) != 4: output("nothing to do, exiting") sys.exit() want="say" room = sys.argv[2] message = sys.argv[3] start() elif sys.argv[1] == "join": if len(sys.argv) == 2: output("nothing to do, exiting") sys.exit() want = "join" room = " ".join(sys.argv[2:]).strip() start() elif sys.argv[1] == "leave": if len(sys.argv) == 2: output("nothing to do, exiting") want = "leave" room = " ".join(sys.argv[2:]).strip() start() elif sys.argv[1] == "messages": want = "messages" start() elif sys.argv[1] in ("pm", "private"): if len(sys.argv) != 4: output("nothing to do, exiting") sys.exit() want = "pm" user = sys.argv[2] message = sys.argv[3] start() elif sys.argv[1] in ( "stats", "info"): if len(sys.argv) > 2: user = " ".join(sys.argv[2:]) else: user = None want = sys.argv[1] start() elif sys.argv[1] == "ip": if len(sys.argv) > 2: user = " ".join(sys.argv[2:]) else: output("nothing to do, exiting") sys.exit() want = "ip" start() elif sys.argv[1] == "browse": if len(sys.argv) > 2: user = " ".join(sys.argv[2:]) else: user = None want = "browse" start() elif sys.argv[1] in ( "transfers", "transfermon"): states = {} states[0] = "Finished" states[1] = "Transferring" states[2] = "Negotiating" states[3] = "Waiting" states[4] = "Establishing" states[5] = "Initiating" states[6] = "Connecting" states[7] = "Queued" states[8] = "Address" states[9] = "Status" states[10] = "Offline" states[11] = "Closed" states[12] = "Can't Connect" states[13] = "Aborted" states[14] = "Not Shared" want = sys.argv[1] start() elif sys.argv[1] == "get": if len(sys.argv) < 3: output("nothing to do, exiting") sys.exit(-1) for url in sys.argv[2:]: if url[:7] == "slsk://": try: user, file = urllib.url2pathname(url[7:]).split("/", 1) print user, file if file[-1] != "/": want = "download" file = file.replace("/", "\\") start() print "Queued %s" % url else: want = "downfolder" file = file.replace("/", "\\") start() print "Getting folder contents %s" % url continue except Exception, e: print e else: print "Invalid soulseek url: %s" % url else: output("no real commands, exiting")