R. Andrews August 29th, 2008 12:17 am

[Link] Toast

So I said I was going to write about Toast "tomorrow", over a week ago. Well, what can I say, I've been busy with actually writing the thing. We're talking OCD-level, every-evening-until-1-AM coding. First, a short recap of my last post: IF (currently) sucks, and one of the ways to fix it is to put it online with Jabber bots. This post is mostly about that last. Naming the thing was unexpectedly hard. There was a period where I was calling it "Vox" until I decided that fun-and-nonthreatening was the way to go, so Toast. There will be a companion program, Jelly, that will act as a sort of server piece for Jabber. Toast is architected so that you can have Toast programs ("responders") talk to almost anything. It comes out of the box with a console runner for testing. Toast (and thus all bots written in Toast) is written in Ruby. Ruby has grown on me. There are warts, but it lets you be more terse than in any language I've seen yet, including Lisp. And it has batteries that Lisp doesn't, like a real object system built in. How do I use it? Anyway, Toast. Here's how to use it: You make a class that subclasses Toast::Responder, called the same thing that the file is in (my_responder.rb contains class MyResponder). Your class has a method called respond, that takes a string, and returns a string. Whatever the first thing your bot is sent (from a particular user) is passed in, and whatever you return is sent back. So, here's a simple bot: class Echo < Toast::Responder def respond msg "you said #{msg}, #{self.from}" end end Not bad, huh? So far, this looks a lot like web development. In fact, it looks a lot like Rails. You define a responder (analogous to a controller in Rails, or a servlet in Java) and it's called when something comes in. Each request gets its own responder. So we have a Jabber app server. But Jabber has something, something big, that the web doesn't (else why bother): statefulness. When a second message comes in from a user, we know it's from the same user. Which means we can send it to the same responder. I've wrapped up a nice API for doing this in two functions: say and wait . say takes a string and sends it back to the user. It's pretty simple, but it means you can send output without returning it, which is to say, without returning from respond . wait is a bit more complex under the hood, but just as simple: it returns the next message the user sends. Which is to say, it waits for another message and then returns it. Here's another short example: class TwoStep < Toast::Responder def respond msg say "first message: #{msg}" second=wait "second message: #{wait}" end end So here we're receiving a message, like before, and printing it out. But we're using say to do that, so we're still in the function. The next line receives the second message from the same user and puts it in a variable, which we then incorporate into the second string, that we return. There are a lot of ways to do this. You could just freeze the whole server until something else comes in, which would make it useless for having more than one user. You could have one thread per user, and freeze that thread until something else comes in, which would make the server use more memory. I chose a third way. If you want to know the trick, I urge you to look at the code, and meditate on it. It's clever. I must admit it was not originally my idea, though using it in this context was. So, having written a little Toast-bot, you would like to test it. No problem! Go into the directory where it's stored and run "toastconsole". This will run your bot from a neat command-line tool with a command history and everything.

How do I get it? So where can you get this amazingness? If you don't already have Ruby, go download it. Then go download the Toast gem and run: gem install toast Voila! Toasty goodness! It even comes with an example, a Hangman game (that sadly only works on the Mac at the moment). Of course, you could also try it out by hitting one of the Toast-running bots when it's online, like randrewsbot@jabber.vond.net. You can send it a message from any Jabber client, including Google Talk. It may go up and down randomly though, as I use it for development. And if you want better documentation, look on the Toast wiki. It's terribly organized at the moment, but getting better.

What's next? Anyway, Toast is about a quarter of the equation. There's also the talk-to-Jabber piece, tentatively called Jelly, that I'm trying to convince someone else to write. There's the game framework (called Iffy) that I am currently writing. Finally, there's the public server portion. Any of these is pretty standalone, and a decent product in its own right. But here's my final plan: Toast can be used to write utilities and services, and Iffy can be used with it to write IF (or IF-like things, although I think it qualifies according to Zarf's definition). Jelly puts Toast online, through Jabber. The public server is a web application that lets you install and manage Toast/Jelly apps. You upload files, the server tells Toast/Jelly to run them in a sandbox, manages a database if you need it, and (eventually) charges you money for the privilege. So it's managed hosting for bots, complete with a rapid development framework. Due to the nature of Jabber, I don't even need to run a Jabber server (although I think I probably will). If you own whatever.com, you can make mobile@whatever.com a Jabber bot, host it on my server, but it looks like your domain. I would charge maybe $5 per month per bot. If you want to, it would be trivial to have a centralized account system (I mean, Jabber users come complete with their own authentication, after all) and allow people to charge a buck a month for access to a bot. Imagine a game released like that, as episodes, one a month. You toss in a couple bucks, play through the first episode on Jabber, if you like it then next month the same bot will have episode two, and you play through that. It'll remember you, of course. Your savegame is in the network, like any webapp, so you can play from anywhere. Imagine a tunnel to a forum like this. You could have the forum (a web app) dispatch Jabber messages when someone replies to your posts. The messages go to the bot, who then messages you. You reply to the message, and the bot HTTP posts the reply on the forum. The forum works like IM for you, but like a web app for everyone else. Imagine an SMS gateway to Toast (in a world where SMS doesn't suck. Maybe an iPhone Jabber client?). Jabber frontends to large databases (imdb, Wikipedia) become really handy. Why load a web page over a crappy radio connection when you can send a bot a few characters and get back what you want? Anyway. What do you think? Want to help? From: vond Date: August 29th, 2008 11:59 am (UTC) (Link) Roxio's Mac disc burning app is called Toast - when they used to have a separate audio CD program, it was called (this is even better) Jam. Reply ) ( Thread From: rbandrews Date: August 29th, 2008 02:40 pm (UTC) (Link) I used to love Toast, actually. Really awesome app. Way better than the "make a burn folder" method. Reply ) ( Parent ) ( Thread From: diadelphous Date: August 29th, 2008 07:52 pm (UTC) (Link) I still think this would be a really cool way for writers interested in digital narratives and digital publishing to get their work out. Story told through IM? THAT'S AWESOME. Or how about a relatively established, mid-list author with a sizeable enough fan base to do something like what Catherynne Valente does, which is allow you to sign up for monthly mailings of exclusive stories and poems and other miscelleny - links would be sent to you via AIM, and bam! Something new to read. It's easier to sneakily be on IM when you're at work, too, so I imagine there's a lot of possibility there, too.



Don't give up! It has a lot of potential for coolness, I think. Reply ) ( Thread From: snej Date: August 30th, 2008 03:24 pm (UTC) (Link) It sounds like you're implementing 'wait' using Ruby's dreaded "continuations" feature? [shivers]



There have been various conversational AIM bots, although none (that I know of) used for IF. The best known was smarterchild, which was run by a startup that wanted to sell the bot-making software ... I think the idea was that they'd be used for online marketing, like an N-Sync bot that you could chat with about how hot Justin was. They ended up changing their business model a few times and then (AFAIK) folding.



Which is not to say that you couldn't make a business, although probably smaller and lower-visibility, out of Jabber bots. Just don't aim your marketing at Fortune 500 companies ;-)



I'm ambivalent about the idea of chatterbots. I love IF, but what seems to happen with bots is that the designers don't put enough effort into the parser, so it ends up being like a remember-and-type CLI shell. If you type "weather san jose" it'll give you the local forecast, but "how's the weather in san jose" or "san jose weather" don't work. That's lame. Don't make that mistake! Reply ) ( Thread From: rbandrews Date: August 30th, 2008 11:45 pm (UTC) (Link) What is wrong with continuations? Reply ) ( Parent ) ( Thread From: snej Date: August 30th, 2008 11:58 pm (UTC) (Link) Nothing wrong with them; I just meant "dreaded" in that they are very powerful but unintuitive.



I haven't looked at your code, but continuations seem like the only way to implement a 'wait' command the blocks but doesn't really block. (I've experimented with doing similar things in Objective-C using the 'ucontext' library, which is a C equivalent of continuations.) Reply ) ( Parent ) ( Thread From: rbandrews Date: August 31st, 2008 05:30 am (UTC) (Link) Yep, callcc is exactly how I do it. It's really not that tricky... You could probably do it with yield also, but I'd have to think about it some.



One thing I miss is that I don't think continuations in Ruby are serializable. In Rhino they are, which means I could freeze continuations to disk, stop the server, restart it, and then load them back up, and (except for a DB connection and filehandles) bots would have no idea anything had happened. Could probably pass them around a cluster too, if you really needed to, but my main thing is stopping the server.



If everything is stored in memory, instead of manually put into a database like Rails, then when the server stops, you're going to lose a lot of session data if you're not careful. Reply ) ( Parent ) ( Thread