[5180] | 1 | # fortunebot.tcl -- |
---|
| 2 | # |
---|
| 3 | # Demo bot using the irc TCL client library. This is a VERY simple bot that |
---|
| 4 | # demonstrates some simple uses of the irc TCL library. |
---|
| 5 | # |
---|
| 6 | # The bot sits on any number of channels and networks. It periodically grabs |
---|
| 7 | # a fortune from a web site and sends the fortune with appropriate delays |
---|
| 8 | # between each line to all channels on all networks it is on. |
---|
| 9 | |
---|
| 10 | # Example use in Tcl: |
---|
| 11 | # |
---|
| 12 | # % source fortunebot.tcl |
---|
| 13 | # % set token [irc::connect ....] |
---|
| 14 | # % fortune::join $token #mychannel |
---|
| 15 | # % fortune::start 120 |
---|
| 16 | # |
---|
| 17 | # What this does: |
---|
| 18 | # * Loads the bot, which in turn will load the IRC library and HTTP. |
---|
| 19 | # * Connect to IRC |
---|
| 20 | # * Send the bot to join #mychannel |
---|
| 21 | # * Start the bot, with 120 seconds delay. Now it'll do its actions every |
---|
| 22 | # 120 seconds. |
---|
| 23 | # |
---|
| 24 | # To stop the bot: |
---|
| 25 | # |
---|
| 26 | # % fortune::stop |
---|
| 27 | # |
---|
| 28 | # Make him leave a channel: |
---|
| 29 | # |
---|
| 30 | # % fortune::leave $token #mychannel |
---|
| 31 | |
---|
| 32 | package require irk |
---|
| 33 | package require http |
---|
| 34 | |
---|
| 35 | namespace eval fortune { |
---|
| 36 | variable state |
---|
| 37 | |
---|
| 38 | array set state { |
---|
| 39 | linedelay 2000 |
---|
| 40 | fortuneurl http://www.earth.com/fortune |
---|
| 41 | } |
---|
| 42 | } |
---|
| 43 | |
---|
| 44 | # Bot control: |
---|
| 45 | |
---|
| 46 | proc ::fortune::start {{delay 60}} { |
---|
| 47 | variable state |
---|
| 48 | |
---|
| 49 | # Compute the delay in milliseconds: |
---|
| 50 | |
---|
| 51 | set state(delay) [expr $delay * 1000] |
---|
| 52 | |
---|
| 53 | # Schedule the bot to run each $delay milliseconds: |
---|
| 54 | |
---|
| 55 | set state(after) [after $state(delay) [list ::fortune::doquote]] |
---|
| 56 | } |
---|
| 57 | |
---|
| 58 | proc ::fortune::stop {} { |
---|
| 59 | variable state |
---|
| 60 | |
---|
| 61 | # Stop the bot if its running: |
---|
| 62 | |
---|
| 63 | if {[info exists state(after)]} { |
---|
| 64 | after cancel $state(after) |
---|
| 65 | unset state(after) |
---|
| 66 | } |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | |
---|
| 70 | # This is the actual body of the bot: |
---|
| 71 | # |
---|
| 72 | # Grab a quote from a web page and post it to all channels we're on: |
---|
| 73 | |
---|
| 74 | proc ::fortune::doquote {} { |
---|
| 75 | variable state |
---|
| 76 | |
---|
| 77 | # Grab the quote. The command callback does all the work: |
---|
| 78 | |
---|
| 79 | http::geturl $state(fortuneurl) -command ::fortune::httpdone |
---|
| 80 | |
---|
| 81 | # Finally reschedule ourselves, after events are one-shots |
---|
| 82 | |
---|
| 83 | set state(after) [after $state(delay) [list ::fortune::doquote]] |
---|
| 84 | } |
---|
| 85 | |
---|
| 86 | proc ::fortune::httpdone {http} { |
---|
| 87 | variable state |
---|
| 88 | upvar #0 $http response |
---|
| 89 | |
---|
| 90 | # Scrape the fortune off of the page: |
---|
| 91 | |
---|
| 92 | set fortune [grabfortune $response(body)] |
---|
| 93 | |
---|
| 94 | # Discard the HTTP array: |
---|
| 95 | |
---|
| 96 | unset response |
---|
| 97 | |
---|
| 98 | # Check if the quote is too long. If it is then punt. |
---|
| 99 | |
---|
| 100 | if {[llength $fortune] > 3} { |
---|
| 101 | return |
---|
| 102 | } |
---|
| 103 | |
---|
| 104 | # Say this quote on all channels on all connections we're on: |
---|
| 105 | |
---|
| 106 | foreach conn [irk::connections] { |
---|
| 107 | tell $fortune $conn |
---|
| 108 | } |
---|
| 109 | } |
---|
| 110 | |
---|
| 111 | # This procedure scrapes the quote off of an HTML page: |
---|
| 112 | |
---|
| 113 | proc ::fortune::grabfortune {body} { |
---|
| 114 | set body [split $body "\n"] |
---|
| 115 | set beg [lsearch $body <PRE>] |
---|
| 116 | set end [lsearch $body </PRE>] |
---|
| 117 | return [lrange $body [expr $beg+1] [expr $end-1]] |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | # This procedure sends the quote to all channels we want the bot to be on: |
---|
| 121 | |
---|
| 122 | proc ::fortune::tell {fort conn} { |
---|
| 123 | variable state |
---|
| 124 | |
---|
| 125 | # Send the fortune to each channel we're on: |
---|
| 126 | |
---|
| 127 | foreach chan [irk::onchannels $conn] { |
---|
| 128 | tellchan $fort $conn $chan |
---|
| 129 | } |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | # Asynchronously send lines to the channel: |
---|
| 133 | |
---|
| 134 | proc ::fortune::tellchan {fort conn channel} { |
---|
| 135 | variable state |
---|
| 136 | |
---|
| 137 | # Check if we are still on the channel: |
---|
| 138 | |
---|
| 139 | if {![irk::onchannel $conn $channel]} { |
---|
| 140 | return |
---|
| 141 | } |
---|
| 142 | |
---|
| 143 | # OK we're still on this channel, so say the current line and schedule |
---|
| 144 | # the next line for later: |
---|
| 145 | |
---|
| 146 | if {[llength $fort] > 0} { |
---|
| 147 | irk::say $conn $channel [lindex $fort 0] |
---|
| 148 | after $state(linedelay) \ |
---|
| 149 | [list ::fortune::tellchan [lrange $fort 1 end] $conn $channel] |
---|
| 150 | } |
---|
| 151 | } |
---|