Skip to content

Category Archives: Scripting

Python Client for Omegle

24-Feb-10

Last weekend I wrote some Python bindings for Omegle and proceeded to write a variety of clients. The first real client I wrote was a multi-user chat room for Omegle. The script starts a few chats, and forwards messages from each to all of the others, prefixing them with "#N#: " (where N is a number assigned to the sender). Next, I made a nicer version of a client which allows one to spy on a conversation between two other users, or interject however one wants. Finally, I wrote a basic Omegle client, which I'll be uploading here.

Though the client is fully usable, it does not yet pass along reCaptcha requests to the user, and I might go back and change how I did the threading. The UI uses curses, only because I wanted some experience with it. I'll definitely post any revisions I do, and I might post the chat room client at some point. I don't think I'll post the spy client. If you have any programming experience, you can build one yourself.

For your enjoyment, a Python Omegle Client.

Senior Project, Application Launcher, System Customizations

11-Jan-10

It has been a while since my last update, and since then we have gotten completely usable two-glove audio output, without accelerometer-based articulations. It is very cool (we can kind of play the Tetris A Theme, but no one is very good at using the gloves yet). Just today we got the accelerometer all working; we have rotation and vertical motion output fit to the correct ranges. Despite having the software all written, we don't have the gloves working with full articulations due to the issue of actually mounting the accelerometers on the gloves and the Arduinos on the wristbands. We have already bought most of the components we'll need to do this, so I expect to have the entire thing working by the end of this weekend. When we finish, I'll post the final parts list, pictures, and latest code, along with instructions for constructing your own pair of musical gloves as a DIY project. Next!

I haven't been very motivated to finish it since the current version works fine, but I'm almost done with a re-write of my application launcher. The new version will by extensible via plugins and will be able to run in the background (as opposed to starting up every time you summon it). Aside from the rewrite, I added some neat shorthand directory launching support, which allows me to open "/home/ifx/Code/Launcher/final" by typing "/lau/f" and pressing enter. Regex is pretty neat.

Finally, I think I'm going to make a post about all the interesting customizations I have made to my Arch Linux install. These would include my mouse gestures (I have some that actually do useful, non-obvious things), my Conky configuration, and, of course, my .bashrc.

Bash, Senior Project, and Application Launcher

24-Oct-09

A while ago I installed a bunch of lib32-* packages as optional dependencies for something. I ended up not needing them for whatever reason (don't remember anymore), so I decided to remove them. The problem was that I also wanted to keep the lib32-* packages that I was actually using. Now, I had my pacman install logs, so I could easily get a list of the specific packages I wanted to remove. If I wanted to be a bit fancier, I could have python parse a section of the logs to generate a remove command to use. This wasn't good enough, though. I decided that I wanted to use a bash one-liner.

After a few hours, I changed my mind. Instead, I have this 7-liner:

TMP1=`mktemp`
TMP2=`mktemp`
yaourt --textonly -Ss lib32-* | grep installed | cut -d "/" -f 2 | cut -d " " -f 1 > $TMP1
yaourt --textonly -Q -t | cut -d "/" -f 2 | cut -d " " -f 1 > $TMP2
comm -12 $TMP1 $TMP2 | xargs yaourt -R
rm $TMP1
rm $TMP2

There might be an easier way, but if you have yaourt installed (this is for Arch Linux users) you can use this little script to remove all the packages matching some search (lib32-* in my case) that aren't required as dependencies for anything (including each other. Run multiple times to get those too.)

Next.

A while ago we received our conductive fabric, and we got nicer output from the accelerometer. It will still need to be "calibrated" at startup to some extent, but this will only require the user to have it not tilted more than 180 degrees from the desired neutral position when the calibration routine is run (which just deals with the first set of outputs from the accelerometer differently from all the others).

And finally:

I have uploaded a working implementation of the application launcher code I wrote a while ago. It doesn't use the panel applet GUI (my end goal), but it does work. Missing features include:

  • "Learning" from previous searches
  • Autodetection of environment (you need to set a few configuration variables manually)
  • Scrolling through results (you can only run the first result at the moment)

All this will be added in time.

Accelerometer Working!

05-Oct-09

Today we got the accelerometer working! We'll need to change how it is set up to get cleaner output, but we do in fact have some output. Also, our conductive fabric (which we had previously been told would be delayed for four weeks) will be here much sooner, though we needed to sacrifice the privilege of using pink fabric for the grey which we are now getting.

Next up is writing the code to use the accelerometer data to produce a rotation and vertical acceleration.

We also decided to document our work with photos, so I'll be uploading photos either to this site or to some photo hosting site at some point.

As a side note, I am also writing a program to use a wacom tablet as instrument with Python + Pygame. Pressure will map to volume, vertical position to frequency, and horizontal position to time until note is played. The interface will be a leftward-scrolling window in which you can draw with the wacom tablet. When whatever you have drawn hits the left side of the window, it will make sound. I'll post more about this later, but if anyone has a good way of getting Wacom Tablet pressure data in Python, feel free to tell me. For some reason reading from /dev/input/wacom (a symlink to whatever /dev/input/event* the tablet is actually mapped to) doesn't work, and I can only get position data using Python's Xlib bindings. It also does not show up as a joystick in Pygame (my laptop's accelerometer does, though), which would have been nice.

Browsing Webcomics with the Arrow Keys

27-Jul-09

So, I just picked up a new webcomic, Gunnerkrigg Court, and found it annoying that I need to switch between using the arrow keys to scoll down the pages (monitor can't fit an entire comic vertically on one screen) and the mouse to click the next button. I decided to write a Greasemonkey script which would solve this problem by allowing me to jump to the next and previous pages by using the arrow keys.

Now revised to work for more than one webcomic.

?View Code JAVASCRIPT
// ==UserScript==
// @name           Navigate Webcomics with the Arrow Keys
// @namespace      www.integerzero.net
// @description    A script which allows you to navigate webcomics with the arrow keys.
// @include        http://www.gunnerkrigg.com/archive_page.php?comicID=*
// @include        http://questionablecontent.net/view.php?comic=*
// ==/UserScript==
 
function getParameter(name2) {
 
   var url = window.location.href;
 
   var paramsStart = url.indexOf("?");
 
 
   if(paramsStart != -1) {
      var paramString = url.substr(paramsStart + 1);
      var tokenStart = paramString.indexOf(name2);
 
      if(tokenStart != -1) {
         paramToEnd = paramString.substr(tokenStart + name2.length + 1);
 
         var delimiterPos = paramToEnd.indexOf("&");
 
         if(delimiterPos == -1) {
 
            return paramToEnd;
         }
         else {
 
            return paramToEnd.substr(0, delimiterPos);
 
         }
 
      }
   }
}
 
var comics = new Object;
comics['www.gunnerkrigg.com'] = Array("http://www.gunnerkrigg.com/archive_page.php?", "comicID");
comics['questionablecontent.net'] = Array("http://questionablecontent.net/view.php?", "comic");
//Add support for more webcomics here
 
var base = comics[location.href.substring(7,location.href.lastIndexOf('/'))][0];
var field = comics[location.href.substring(7,location.href.lastIndexOf('/'))][1];
var num = getParameter(field);
 
 
num = parseInt(num);
 
p = (num-1)+'';
 
n = (num+1)+'';
 
 
function handleArrowKeys(evt) {
   evt = (evt) ? evt : ((window.event) ? event : null);
 
   if (evt) {
 
      if (evt.keyCode == 37) {
 
         window.location = base+field+"="+p;
      }
      else {
         if (evt.keyCode == 39) {
            window.location = base+field+"="+n;
 
    		}
    	}
   }
}
 
 
window.addEventListener('keyup', handleArrowKeys, true);
 
document.onkeyup = handleArrowKeys;

The script is available here at userscripts.org.

Managing Music from the Command Line

30-Jun-09

Okay. I've been curious for a while about trying to manage my music from the command line. All the media managers I have used so far have been a bit too bloated for my tastes. Because of this I decided to try out MPD (Music Player Daemon). It looked pretty cool, but I didn't have a good way to quickly queue up a bunch of songs I wanted to listen to. After trying out a few solutions by others, I wrote two of my own solutions.

The first was a fuzzy matching system, and the second was a "blob" matching system. Both allow me to queue up an entire album by typing something short.

Because I haven't finished tweaking the first solution, and the second one is working impressively, I'll be posting about it only.

What You'll Need:

  • mpd
  • mpc
  • Python 2.6

Here is the code:

?View Code PYTHON
#!/usr/bin/python
from subprocess import Popen, PIPE
import sys
 
class Database:
	def __init__(self, terms=None):
		self.terms = set()
		self.tree = (set(), dict())
		if terms is not None:
			self.populate(terms)
 
	def populate(self, terms):
		for term in terms:
			self.add(term)
 
	def variations(self, word):
		result = set([word])
		if word.startswith("0") and word != "0":
			#This allows "02" to be found as "2"
			result.add(word)
		elif word in frozenset(["i", "ii", "iii"]):
			#This allows "II" to be found as "2"
			result.add(str(len(word)))
		return result
 
	def words(self, term):
		term = term.lower()
		words = set()
		buf = ""
		for char in term:
			if char.isalnum():
				buf += char
			else:
				if buf != "":
					words = words.union(self.variations(buf))
					buf = ""
		if buf != "":
			words = words.union(self.variations(buf))
		return words
 
	def add(self, term):
		self.terms.add(term)
		for word in self.words(term):
			current = self.tree
			for char in word:
				if char not in current[1]:
					current[1][char] = (set(), dict())
				current = current[1][char]
			current[0].add(term)
 
	def search(self, blob, union=False):
		#This is for excluding words from a search
		if "~" in blob:
			#Excludes titles with ANY words from exclude
			blob, remove = blob.lower().split("~")
			mode = True
		elif "#" in blob:
			#Excludes titles with ALL words from exclude
			blob, remove = blob.lower().split("#")
			mode = False
		else:
			remove = None
			blob = blob.lower()
		found = dict()
		current = self.tree
		i = 0
		for char in blob:
			if char in current[1]:
				current = current[1][char]
				i += 1
				if len(current[0]) > 0:
					found[blob[i:]] = current[0]
			else:
				break
		final = set()
		for k, v in found.items():
			if k == "":
				final |= v
			else:
				if union:
					final |= v | self.search(k)
				else:
					final |= v & self.search(k)
		if remove is not None:
			remove = self.search(remove, mode)
			final -= remove
		return final
 
	def __contains__(self, other):
		return other in self.terms
 
#Initialize and populate album database
db = Database()
#with open("/var/lib/mpd/tag_cache") as f:
#	for line in f:
#		if line.startswith("Album: "):
#			album = line[7:-1]
#			if album not in db:
#				db.add(album)
#We'll get the album list from mpc instead of the tag_cache file. I haven't done a benchmark.
p1 = Popen(["mpc", "list", "album"], stdout=PIPE)
p1.wait()
with p1.stdout as f:
	for line in f:
		album = line[:-1]
		if album != "":
			db.add(album)
 
#Parse arguments
if len(sys.argv) == 2:
	searches = [sys.argv[1]]
else:
	print "No album specified. Exiting..."
	sys.exit()
 
#Perform search
r = db.search(searches[0])
r = list(r)
 
#If more than one album is returned, ask the user to choose which one we will play.
if len(r) > 1:
	for i in xrange(len(r)):
		print " " + str(i+1) + ")", r[i]
	r = r[int(raw_input("Enter a number: ")) - 1]
elif len(r) == 1:
	r = r[0]
else:
	print "No albums found. Exiting..."
	sys.exit()
 
print "Playing album '" + r + "'"
 
#Find the tracks in the album
p1 = Popen(['mpc', 'find', 'album', r], stdout=PIPE)
p1.wait()
 
#Clear the current playlist
Popen(['mpc', 'clear'], stdout=PIPE).wait()
 
#Queue up all the tracks in the album
with p1.stdout as f:
	for line in f:
		Popen(['mpc', 'add', line[:-1]], stdout=PIPE).wait()
 
#Let MPC print a list of the tracks we have queued
Popen(['mpc', 'playlist']).wait()
 
#Start playback
Popen(['mpc', 'play'], stdout=PIPE).wait()

Save that as "album" or whatever you would like. Put it in your home directory or wherever you would like. Finally, open ~/.bashrc (for Ubuntu users, at least), and add an alias pointing to the script. I use "alias album='~/album'".

Make sure you set the script to executable.

Finally, because I enjoy having my listening history available on last.fm, I also installed lastmp to scrobble songs played with MPD.

Oh yeah. I almost forgot usage instructions. With the setup described above, one could just type "album wolfsrain" to play the Wolf's Rain OST (the script would prompt you to select either the first or second volume) or "album fiction" to play Yuki Kajiura's solo album "Fiction".

I call this album search method a "blob search" because you can type any words from an album title, in any order and without spaces, and still find the album. For instance, both "album wishyou" and "album hereyouwish" will start the playback of Wish You Were Here in my library.

TIDY Music Folder Cleaner 1.4

14-Jan-09

TIDY MFC 1.4 has been rebuilt and solidly tested to work on Windows XP as well as Windows Vista. I am working on converting this to a simple c++ app to ensure that it will work on future versions of Windows (like 7 and up). As it is, I am currently working on an update for Windows 7, as I am testing it out.

TIDY-1.4.zip (hosted by The League of Magnificent Scoundrels)

TIDY Music Folder Cleaner 1.4 is a small batch file that allows you to delete all hidden .ini files as well as all JPEG, .db files, and unwanted playlist files you may not have.

In order for TIDY Music Folder Cleaner to work properly, you must put it in your "Music" (Vista) or "My Music" (Windows 2000/XP) folder. Running it from there will check that folder and every folder inside it, so you don't have to spend hours going through your collection to delete unwanted files.

Just make sure that if you want to keep your playlists, you should not put them in the music folder, but instead put them in a folder to themselves. This not only keeps them organized, but it also puts them all where you'll remember them.