[22:12] so if people are interested, I can give a tour of writing yardbird apps [22:12] easiest if you know the djangos [22:12] but not required [22:12] brb [22:12] * rasher doesn't know the djangos [22:12] How do I kill things in this mud? [22:13] I can read it in scrollback, but I will be seeking burritoids soon [22:16] squinky: boo [22:16] I've sent scrollback logs of this sort of tour to other people, and it always just becomes TLDR [22:17] rasher: okay well the important bits are really at the top of [22:17] http://zork.net/motd/nick/django/introducing-yardbird.html [22:17] sorry [22:17] top of http://zork.net/motd/nick/django/your-favorite-orm-sucks.html [22:18] though http://zork.net/motd/nick/django/introducing-yardbird.html is a pretty decent read too [22:18] but don't be distracted about the twisted bullshit [22:19] the whole point of yardbird is that I realized that I could bottle up six lines of twisted python, shove them into one method, and hook the whole rest of the bot up to django [22:19] Should I just plow through all of it? [22:19] so all you, as a bot author, see is the djangos and my own API [22:20] rasher: might as well, while we wait for squinky [22:20] * rasher runs off [22:20] I'm going to keep hacking on this bug [22:20] :< [22:20] wrong answer? [22:20] No I mean, to read [22:21] Stand back, guys, I'm a professional Djangoist. [22:22] ah cool [22:22] atob: succulent. [22:34] * SpaceHobo tests a fix [22:34] yardbird: reload [22:34] -yardbird:#yardbird- Reload successful. [22:34] yardbird: + is addition [22:34] Sorry, SpaceHobo. [22:34] yardbird: . is a dot [22:34] Sorry, SpaceHobo. [22:34] yardbird: .. are two dots [22:34] Sorry, SpaceHobo. [22:35] okay cool [22:35] yardbird: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa is a long key [22:35] -yardbird:#yardbird- ERROR: value too long for type character varying(64)PnPnINSERT INTO "iotower_factoid" ("fact", "protected") VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', False) [22:35] ... [22:35] oi [22:35] -yardbird:#yardbird- ERROR: current transaction is aborted, commands ignored until end of transaction blockPnPnSET client_encoding to 'UNICODE' [22:35] -yardbird:#yardbird- ERROR: current transaction is aborted, commands ignored until end of transaction blockPnPnSET client_encoding to 'UNICODE' [22:35] -:- Signoff yardbird: #yardbird (Input/output error) [22:35] grrr [22:35] need to make that roll back somehow [22:36] hmmmmm [22:36] should have worked [22:36] -:- yardbird [~yardbird@cloak-2534BC16.zork.net] has joined #yardbird [22:37] what up, meatbags [22:37] yardbird: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa is a long key [22:37] -yardbird:#yardbird- ERROR: value too long for type character varying(64)PnPnINSERT INTO "iotower_factoid" ("fact", "protected") VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', False) [22:37] -:- Signoff yardbird: #yardbird (Input/output error) [22:38] okay time to make my own execption then [22:38] * rasher has sort of read it all now [22:38] rasher: okay, any pressing questions before we begin? [22:39] -:- yardbird [~yardbird@cloak-2534BC16.zork.net] has joined #yardbird [22:39] yardbird: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa is a long key [22:39] what up, meatbags [22:39] -yardbird:#yardbird- ERROR: value too long for type character varying(64)PnPnINSERT INTO "iotower_factoid" ("fact", "protected") VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', False) [22:39] -:- Signoff yardbird: #yardbird (Input/output error) [22:39] yeah fuck you [22:39] Not as such.. I'm sure there are things I don't understand, but I think it's easier to just go on [22:39] oh right [22:39] I botched the test [22:40] okay [22:40] atob: you playing along? [22:41] * rasher has never tuched the django [22:41] sure [22:41] just remember the four elements I listed at the start of my ORM rant [22:41] so step one is to get the yardbird code [22:41] bzr get lp:yardbird [22:43] I'll wait [22:43] yeah sorry, got distracted by the launchpad id thing [22:43] got it [22:43] launchpad id? [22:43] You have not informed bzr of your Launchpad ID, and you must do this to [22:43] write to Launchpad or access private data. See "bzr help launchpad-login". [22:43] oh I see [22:44] yeah it'll do http unless you do "bzr launchpad-login" [22:44] wut [22:44] it should just do http [22:44] It does, but it whined [22:44] okay so this archive has three subdirs [22:44] two are apps and one is a "project" [22:45] example/ is the "project" which basically means it's just a sample of how you set up the bot's settings [22:45] yardbird/ is the yardbird app, which does all the IRC stuff and provides an API for you to write useful code [22:45] and for historic reasons I included one yardbird app in here, iotower [22:45] iotower is the infobot clone [22:46] so let's start with the example/ [22:47] in there you've got a manage.py script, [22:47] and a settings.py [22:47] all this is pretty stock django, settings.py lets you specify a db, time zone, admins... [22:47] down at the bottom I've set some IRC_* settings [22:47] like channels and servers [22:48] you'll notice I also added yardbird and iotower to the INSTALLED_APPS tuple [22:48] Yoink [22:48] this is basically a stock "django-admin startproject" that i poked at to work with yardbird [22:49] Looks nice [22:49] okay, so when I do something like... [22:49] MikeWallace: stats [22:49] SpaceHobo: Since 2009-08-03 15:45:30 I have performed 1445 edits on 1425 factoids containing 1433 active responses [22:49] I suppose I don't have to care much what TEMPLATE_LOADERS and MIDDLEWARE_CLASSES are [22:49] yeah that's stock http crap [22:49] ignore that noise [22:49] sorry [22:50] you were saying [22:50] so when you enter a command, the first thing is the dispatcher [22:50] so traditional django uses urls.py [22:50] to match regexes against the urls that come in [22:50] and use those to pass off the HTTP request to a function called a "view" [22:51] you'll notice that urls.py here directs to a built in admin site [22:51] which is django's web-based sql database manager [22:51] so if you hooked it up to a web server it'd present a db manager? [22:52] but IRC works slightly differently [22:52] yep [22:52] (vaguely remembering from some of your rants) [22:52] you can even run the built-in doodad [22:52] ./manage.py syncdb [22:52] then ./manage.py runserver [22:52] and it'll give you a web server on port 8000 of localhost [22:53] cvte [22:53] yeah [22:53] meanwhile I have added ./manage.py runircbot [22:53] which will cause the thing to connect here [22:53] anyway, instead of URLs, IRC has different types of incoming messages [22:53] events, really [22:53] privmsgs, actions, topic changes, nick changes. joins, parts... [22:54] and that's not even getting into messy stuff like DCC [22:54] * rasher doesn't expect to ever ever ever want a bot that does dcc [22:54] srsly [22:54] so notice that i've got a privmsg.py [22:54] and symlinked action.py and nick.py [22:54] MikeWallace: bot [22:54] I don't know what you mean, SpaceHobo. [22:54] MikeWallace: emad [22:54] I don't know what you mean, SpaceHobo. [22:54] MikeWallace: nice [22:54] I don't know what you mean, SpaceHobo. [22:54] oh hell [22:55] MikeWallace: emad is as emad does [22:55] Roger that, SpaceHobo. [22:55] -:- SpaceHobo is now known as emad [22:55] -:- emad is now known as SpaceHobo [22:55] 21:55 #proclick:[28]: emad is as emad does [22:55] heh, he saw the nick change in #proclick first [22:55] but you get the point [22:55] MikeWallace: emad [22:55] emad is as emad does [22:55] I've made nicks equivalent to commands [22:55] probably not a good idea in the long run [22:55] -:- SpaceHobo is now known as stats [22:55] -:- stats is now known as SpaceHobo [22:55] Yeah I imagine that could be confusing [22:56] bu it's fun for testing [22:56] linking action is good tho [22:56] It's not seeing the nick changes for each channel? [22:56] * SpaceHobo emad [22:56] emad is as emad does [22:56] apparently not [22:56] anyway, moving on [22:56] privmsg.py looks a lot like urls.py except the regexes are bizarre [22:57] because instead of being urls, it's all kinds of irc commands [22:57] in this example, the first one is the stats command [22:57] MikeWallace: stats [22:57] SpaceHobo: Since 2009-08-03 15:45:30 I have performed 1453 edits on 1433 factoids containing 1441 active responses [22:57] stats [22:57] which basically saves off the addressee and passes that as a named argument to yardbird.contrib.stats.gather_statistics [22:57] (I guess IRC only sends one nickchange command. Suppose some faking would be good, somewhere) [22:58] huh [22:58] anyway [22:59] the second regex in privmsg.py says "save off the addressee and pass the rest of the incoming text to the iotower/commands.py" [22:59] this include() function was originally made so that you could have, say, a gallery app with viewimg/(?...) [22:59] and you could load it under some space [23:00] like (r'my/deep/site/with/lots/of/structure', include(gallery.stuff)) [23:00] but I only use it to strip off the addressee once and throw the rest at iotower [23:00] I don't think many other apps will use this trick [23:00] since only the last one really can [23:00] or maybe you have hierarchical prefix commands [23:01] other bots do this, like supybot [23:01] * SpaceHobo shrugs [23:01] okay, so let's go look at ../iotower/commands.py [23:01] this gets a bit hairier [23:01] I've tried to comment it though [23:01] So you mean rather split up things in privmsg, rather than the app? [23:02] well I mean like [23:02] you could set up, say, a google app [23:02] Yeah, yes [23:02] Rather than an "everything" app [23:02] and do (r'^google', include(google.commands)) [23:02] and google.commands would have search and calc and so forth [23:02] maps [23:02] whatever [23:02] so when you typed [23:02] google search blah blah blah [23:03] the first word would send you into the google app, which would catch on search and send the rest in as arguments to whatever function search was bound to [23:03] * rasher boggles a bit at iotower/commands.py [23:03] yeah, take line 16 [23:03] what's emad? [23:03] emad is as emad does [23:04] (?iux) just means case-insensitive, unicode, and ignore whitespace in the regex (require \s to indicate space) [23:04] so I match on ^what's followed by arbitrary whitespace [23:04] then save off a bunch of stuff into "key" and throw away trailing punctuation/whitespace [23:04] yeah? [23:05] And trigger gets a dict with 'key':'emad'? [23:05] well [23:05] somewhere [23:05] that actually calls iotower.ircviews.trigger(ircrequest, key='emad') [23:05] (line 3 is where it gets the iotower.ircviews) [23:06] and the ircrequest object is implicit [23:06] oh well, close enough [23:06] yeah [23:06] same effect [23:06] since key args really are dicts [23:06] so all this stuff does is match "commands" and pass chunks of the text in as keyword arguments to these functions [23:06] let's go into iotower/ircviews.py [23:06] this is where all the work is done [23:07] line 79 [23:07] is where the trigger function is [23:07] 82 here? [23:07] 79 has a fixme :) [23:07] 79 def trigger(request, key='', verb='', **kwargs): [23:07] 76 # FIXME: This should be an edit conflict exception [23:07] oh I'm out of date [23:07] hangon [23:08] yes, 82 [23:08] okay [23:08] so like I said, this view function gets passed in an irc request object (which we'll look at soon) [23:08] and the positional arguments [23:09] and because these can be lots of things I just catch any odd ones and ignore in **kwargs [23:09] so first I try and get the factoid out of the database, using this nice django shortcut function [23:10] if it can't find a Factoid object in the db for which its "fact" column case-insensitive matches the key we got (after removing whitespace and punctuation) [23:10] then it raises an Http404 exception [23:10] which I catch in yardbird itself and it gives this: [23:10] MikeWallace: WHAT IS LOVE [23:10] I don't know what you mean, SpaceHobo. [23:10] ^-- actually a 404 error [23:10] :> [23:10] yeah [23:11] so then we see it trying to follow some attributes of this Factoid object it got [23:11] this here is django ORM stuff [23:12] factoid.factoidresponse_set is a query that will give you a list of all the FactoidResponse objects whose foreign keys point to factoid [23:12] we then filter it by requiring that the verb contains our verb, and that it isn't disabled [23:13] then order_by ? just means RANDOM [23:13] and then the moment i actually try to get something out of the result by doing the [0] POW then it actually runs [23:13] all the other foo.bar.filter.refine.wut.refine stuff [23:13] just built query objects [23:13] only once you do something like get .all() or try to slice it or read from it [23:14] only then does it send SQL over the wire [23:14] if you're curious, the schema for the Factoid and FactoidResponse tables is in iotower/models.py [23:14] and is pretty straightforward stuff [23:14] take a look for a bit [23:15] I suppose this would be more straightforward if I knew django already [23:15] each class is a table [23:15] I'm a slightly stumped by the query object building [23:15] each class attribute is a column [23:15] yeah the query language is its own thing [23:15] and that example is kind of annoying [23:15] just think of it this way [23:16] each function called returns a query object [23:16] so like a is a query [23:16] a.filter(name='rasher') returns a new query object formed from a that adds the filter constraint [23:16] so a.filter(name='rasher').filter(type='pig') [23:17] is just calling functions that return objects with the same functions in [23:17] it's called Literate style, for some reason, although it has nothing to do with Literate Programming [23:18] anyway, back to ircviews.py [23:18] line 102 sets up a dictionary [23:18] 102 d = {'factoid': key, 'verb': text.verb, 'text': rendered} [23:19] key, verb, response text [23:19] MikeWallace: emad [23:19] emad is as emad does [23:19] ^-- {'factoid': 'emad', 'verb': 'is':, 'text': 'as emad does'} [23:19] MikeWallace: literal emad [23:19] emad =is= as emad does [23:19] lets you see the = around the verb [23:19] so now it's time to render it to a template [23:19] using render_to_response [23:20] we have some logic to determine which template [23:20] different ones for actions and replies Ithink [23:20] but the important thing is that 107 renders a response [23:20] first argument is the recipient, and we just ask the ircrequest object which recipient would be appropriate for a reply to this request [23:21] in a public channel, that's the channel [23:21] in privmsg that's the user [23:21] then the template [23:21] then the dictionary of values [23:21] and then the method, which just says if the bot will use /me or /topic or /msg or whatever [23:21] if you look in templates/ you'll see the factoid.irc template [23:21] {% extends "base.irc" %}{% block message %}{{factoid}} {{verb}} {{text}}{% endblock %} [23:21] ^-- pretty simple [23:22] it inherits from a core base.irc template that just shuts off html entity escaping crap [23:22] Simpler than your average web template :) [23:22] yeah [23:22] single line [23:22] but [23:22] but is he around now [23:22] you can do multi-line [23:22] for a while I had yardbird doing monoculatus for all factoids :) [23:22] ┌─┐ [23:22] ┴─┴ emad is as emad does [23:22] ಠ_̼ರೃ [23:22] etc [23:23] Haha [23:23] ┴─┴ emad [23:23] ┴─┴ emad is as emad does [23:23] Quite. [23:23] Fantastic [23:23] so most of what you do is in views, making use of models [23:24] so figuring out the ORM is a good idea, and the django tutorials are pretty good at that [23:24] but take a look at yardbird/irc.py [23:24] simple request and response objects [23:24] basically moderately smart structs [23:26] those look simple enough [23:28] they're basically your packet format for communication between view functions and the bot framework [23:28] the yardbird.shortcuts stuff usually is just quick routes to building an IRCResponse [23:28] and everybody gets an IRCRequest as the first argument [23:35] rasher: so what would you want to do with an irc bot? [23:36] like, what's an example app you'd want to write