AIM TOC protocol foundation
Posted by Parasite-FT-
Friday, July 23, 2004
Description: a foundation for a full-blown AIM script

A lot of people have decided to give the AOL Instant Messenger protocol a shot, but for understandable reasons quite a few people have had difficulties with it all. I'm not using OSCAR (it changes too often and is quite unneccessary), instead I'm using the TOC protocol which is a level above OSCAR.

You can find an AIM script from ms.org, but it's riddled with bugs and the coding is incomprehensible (and, if you ask me, it's pretty sloppy). My aim is to create a foundation for other people to create a GUI with - that is, this should end up being just a backbone for you to put a nice interface on. We shall see.

Feel free to add on to this or modify it as you like - hopefully I added enough comments (prolly too much in fact), but if you have any questions feel free to ask.

Note: this is using binvars pretty heavily, you should prolly get accustomed to using those first before attempting to edit this.

Changes log:
July 24, 2004 - I just added this so there are still echo's in it for debugging purposes. The login is now complete below, however nothing else has been added yet.
July 25, 2004 - The login bug was fixed. Also added the ability to recieve messages. Decided upon using an 'API'; a series of aliases acting as an interface for people to build upon this without editing it.
July 25, 2004 - Major changes; optimized roaster, added ability to message, format nick, warn people, receive messages, get info from users, change password, set/remove away message. Moved everything that wasn't part of the core protocol out of the file, will make a help file to explain integration with scripts.
July 26,2004 - Added ability to get user profiles and display them in a picwin for a sample GUI. the core itself is near completion.
August 8, 2004 - Changed the interface, and added some features to the protocol foundation. Still looking to finish a picwin scrollbar, then i'll integrate it into the GUI and be ready to finish the damned thing.


;http://gridley.res.carleton.edu/~straitm/final/howto_signon.html
;http://www-eleves-isia.cma.fr/Doc/packages/kaim/PROTOCOL

;; =========================================================
;; Connection aliases

;; Function: connects to the TOC server
;; Usage: /aim.connect <username> <password>
alias aim.connect {
  sockclose aim
  ;:::  toc.oscar.aol.com isn't very stable I find - but this host works great methinks
  var %host = 64.12.163.175, %port = 1024
  ;::: cycle through port numbers until a free one is reached
  while (!$portfree(%port)) { inc %port }
  sockopen aim %host %port
  aim.add username $1
  aim.add password $$2
  aim.add sequence_number 1
}

;; Function: disconnects from the TOC server
;; Usage: /aim.disconnect
alias aim.disconnect { sockclose aim }

;; Function: parses the server reply
;; Usage: /aim.parse <&binvar>
;; Comments: prolly the messiest alias here, but when was parsing ever clean?
alias -l aim.parse {
  ;::: loop through the all the FLAP segments provided (multiple data segments often sent in one packet)
  while (1) {
    var %type = $aim_flap($1).type
    var %length = $aim_flap($1).len
    var %number = $aim_flap($1).number
    ;::: make a binvar to hold ONLY the current FLAP segment data (no header)
    if (%length > 0) bcopy -c &flap_data 1 $1 7 %length
    ;::: debug information
    if ($isalias(aim.API_debug)) {
      aim.API_debug Flap type: %type
      aim.API_debug Flap length: %length
      aim.API_debug Flap number: %number
      aim.API_debug Flap data: $bvar(&flap_data,1,$bvar(&flap_data ,0))
    }
    ;::: send to the redirect alias
    aim.redirect %type &flap_data
    ;::: crop the binvar to remove the segment we just used
    if ($bvar($1,0) > $calc(%length + 7)) bcopy -c $1 1 $1 $calc(%length + 7) -1
    ;::: check to see if we've looped till the end of the data (%length + 7 is amount of bytes in data + header)
    else break
  }
}

;; Function: redirects data to the proper event handler
;; Usage: /aim.redirect <FLAP type> <&binvar holding data>
alias aim.redirect {
  var %type = $1, %data = $2
  ;::: login data
  if (%type == 1) aim.login
  ;::: handle all other data
  elseif (%type == 2) {
    ;::: find the first colon in the binvar and retrieve the item name for event handling
    var %item_end = $calc($bfind(%data,1,58) - 1)
    var %item = $bvar(%data,1,%item_end).text
    ;::: crop out the item name from the data
    bcopy -c %data 1 %data $calc(%item_end + 2) -1
    ;::: redirect to event handlers
    if (%item == SIGN_ON) aim.on_login
    elseif (%item == NICK) aim.on_nick %data
    elseif (%item == CONFIG) aim.on_config %data
    elseif (%item == ERROR) aim.on_error %data
    elseif (%item == IM_IN) aim.on_message %data
    elseif (%item == GOTO_URL) aim.on_getinfo %data
    elseif (%item == UPDATE_BUDDY) aim.on_buddy %data
    elseif (%item == EVILED) aim.on_warn %data
  }
  ;::: Connection terminated event
  elseif (%type == 4) aim.on_terminate
  ;::: PING-PONG event
  elseif (%type == 5) aim.on_ping
}

;; Function: creates/sends the required login information
;; Usage: /aim.login
;; Comments: This is a two part login. First send the SIGNON message, followed by the "toc_signon" message.
;;           For the sake of simplicity I'll send both messages from this one alias.
;;           Sadly these values must be sent using a binvar so it may look confusing but it's quite straightforward
alias -l aim.login {
  var %password = $aim_roast($aim_get(password))
  var %username = $aim_normalize($aim_get(username))
  var %sequence_number = $aim_get(sequence_number)

  ;::: send a FLAP SIGNON message
  ;-----------------------------------------
  ;::: FLAP version with value of '1' (4 bytes)
  bset &login_flap 1 0 0 0 1
  ;::: TLV tag with value of '1' (2 bytes)
  bset &login_flap 5 0 1
  ;::: username length (2 bytes)
  bset &login_flap 7 0 $len(%username)
  ;::: username value (specified length)
  bset -t &login_flap 9 %username
  var %login_flap = $aim_buildflap(1,&login_flap)
  sockwrite aim %login_flap

  ;::: send a "toc_signon" message
  ;----------------------------------------
  ;::: cycle through port numbers until a free one is reached
  var %port = 29999
  while (!$portfree(%port)) { inc %port }
  ;::: toc_signon <authorizer host> <authorizer port> <User Name> <Roasted Password> <language> <version>
  bset -t &toc_signon 1 toc_signon login.oscar.aol.com %port %username %password English $+("mIRC,TIC,")
  ;::: create a FLAP header for &toc_signon (type 2 for some reason)
  var %flap = $aim_buildflap(2,&toc_signon).null
  ;::: send the data
  sockwrite aim [ [ %flap ] ]
}

;; Function: sends the specified string to the TOC server
;; Usage: /aim.out <text>
alias aim.out {
  bunset &output
  bset -t &output 1 $1-
  var %output = $aim_buildflap(2,&output).null
  if ($sock(aim)) sockwrite aim %output
  if ($isalias(aim.API_debug)) {
    aim.API_debug Output [byte format]: $bvar(%output,1,$bvar(%output,0))
    aim.API_debug Output [text format]: $bvar(%output,7,$bvar(%output,0)).text
  }
}

;; =========================================================
;; Connection events

;; Function: establishes an initial connection
on *:sockopen:aim: {
  if ($sockerr) return
  sockwrite -n $sockname FLAPON
  sockwrite -n $sockname
}

on *:sockread:aim: {
  sockread &temp
  aim.parse &temp
}

;; Function: retrieves the information for a getinfo
on *:sockopen:aim.getinfo.*: {
  sockwrite -n $sockname GET / $+ $sock($sockname).mark HTTP/1.1
  sockwrite -n $sockname HOST: 64.12.163.175
  sockwrite -n $sockname Connection: close
  sockwrite -n $sockname
}

on *:sockread:aim.getinfo.*: {
  sockread &temp
  ;::: crop out the header information
  bcopy -c &temp 1 &temp $calc($bfind(&temp,1,10 13) + 3) -1
  if ($isalias(aim.API_debug)) aim.API_debug Incoming getinfo response: $bvar(%output,1,$bvar(%output,0))
  if ($isalias(aim.API_getinfo)) aim.API_getinfo &temp
}

;; =========================================================
;; event handlers

;; Function: handles the CONFIG request by TOC server (buddy list)
;; Usage: /aim.on_config <&binvar>
alias -l aim.on_config {
  var %end_byte = 1, %item
  ;::: loop through all the buddy's in the list (tokens delimited by chr 10)
  while ($bfind($1,1,10)) {
    %end_byte = $ifmatch
    ;::: grab the buddy/group name; spans bytes 1 through %end_byte - 1
    %item = $bvar($1,1,$calc(%end_byte - 1)).text
    ;::: parse out the type and name from the item (item looks like: g Group, or, b Buddyname)
    var %item_type = $gettok(%item,1,32), %item_name = $gettok(%item,2-,32)
    ;::: send information to the API
    if ($isalias(aim.API_createbuddylist)) aim.API_createbuddylist %item_type %item_name
    ;::: check to see if user is online or not
    if (%item_type == b) aim.out toc_add_buddy %item_name
    ;::: if the end-of-data has not yet been reached, crop the current information from the binvar
    if ($calc(%end_byte + 1) < $bvar($1,0)) bcopy -c $1 1 $1 $calc(%end_byte + 1) -1
    ;::: once the loop has exceeded all the available data, break from the loop
    else break
  }
}

;; Function: catches a message from a user
;; Usage: /aim.on_message <&binvar>
alias -l aim.on_message {
  ;::: the user is between 1 and the first chr 58 found
  var %user = $bvar($1,1,$calc($bfind($1,1,58) - 1)).text
  ;::: the automated response value is 1 char after the length of username + 2; T or F
  var %automated_response = $bvar($1,$calc($len(%user) + 2),1).text
  ;::: crop the message from the original binvar
  bcopy -c $1 1 $1 $calc($len(%user) + 4) -1
  if ($isalias(aim.API_incomingmessage)) aim.API_incomingmessage $+(%user,:,%automated_response,:,$1)
}

;; Function: catches a response to a getinfo for the user
;; Usage: /aim.on_getinfo <&binvar>
;; Comments: open a new connection to send a basic HTTP request for the URL that is given by the response
alias -l aim.on_getinfo {
  ;::: format for response is TIC:<relative url> - the response is small enough to use .text prop of $bvar
  var %url = $gettok($bvar($1,1,$bvar($1,0)).text,2,58)
  var %socket = aim.getinfo. $+ $ticks
  sockopen %socket 64.12.163.175 80
  sockmark %socket %url
}

;; Function: shows a users updated status/shows a user if online/offline
;; Usage: /aim.on_buddy <&binvar>
alias -l aim.on_buddy {
  ;::: format: UPDATE_BUDDY:<Buddy User>:<Online? T/F>:<Evil Amount>:<Signon Time>:<IdleTime>:<UC>
  var %text = $bvar($1,1,$bvar($1,0)).text, %args = $gettok(%text,-1,58)
  ;::: check to see if user is on AOL network
  var %onAOL = $iif($mid(%args,1,1) == A,$true)
  ;::: check to see if the user has normal or unconfirmed internet type (admin type is skipped, i'm lazy)
  var %connection = $iif($mid(%args,1,1) == O,normal,unconfirmed)
  ;::: check to see if the user has an away message
  var %away = $iif($mid(%args,2,1) == U,$true)
  ;::: replace the UC param with it's parsed meanings, each delimited by :
  var %newparams = $puttok(%text,$+(%onAOL,:,%connection,:,%away),6,58)
  if ($isalias(aim.API_debug)) aim.API_debug Update buddy: %newparams
  if ($isalias(aim.API_updatebuddy)) aim.API_updatebuddy %text
}

alias -l aim.on_warn {
  ;::: format: <new evil>:<name of eviler, blank if anonymous>
  var %text = $bvar($1,1,$bvar($1,0)).text
  tokenize 58 %text
  if ($isalias(aim.API_warn)) aim.API_warn $1 $2
}

;; Function: complete the login sequence
;; Usage: /aim.on_login
alias aim.on_login {
  ;::: required for everyone to see that you're online
  aim.out toc_add_buddy ''
  ;::: completes the login sequence
  .timer 1 1 aim.out toc_init_done
}

;; Function: stores what your current nick is formatted like
;; Usage: /aim.on_nick <&binvar holding nick>
alias -l aim.on_nick {
  var %nick = $bvar($1,1,$bvar($1,0)).text
  aim.add nick %nick
}

;; Function: keep the connection alive
;; Usage: /aim.on_ping
alias -l aim.on_ping { if ($isalias(aim.API_debug)) aim.API_debug PING: PONG! }

;; Function: terminate the connection
;; Usage: /aim.on_terminate
alias -l aim.on_terminate {
  if ($isalias(aim.API_debug)) aim.API_debug Connection: terminated
  if ($isalias(aim.API_disconnect)) aim.API_disconnect
}

;; Function: when the server throws an error send an error message to the API
;; Usage: /aim.on_error <&binvar>
alias -l aim.on_error {
  if (!$isalias(aim.API_error)) return
  var %message = $bvar($1,1,$bvar($1,0)).text
  tokenize 58 %message
  ;::: General Errors
  if ($1 == 901) aim.API_error $2- not currently available
  elseif ($1 == 902) aim.API_error Warning of $2- not currently available
  elseif ($1 == 903) aim.API_error A message has been dropped, you are exceeding the server speed limit
  ;::: Admin Errors
  elseif ($1 == 911) aim.API_error Error validating input
  elseif ($1 == 912) aim.API_error Invalid account
  elseif ($1 == 913) aim.API_error Error encountered while processing request
  elseif ($1 == 914) aim.API_error Service unavailable
  ;::: Chat Errors
  elseif ($1 == 950) aim.API_error Chat in $2- is unavailable.
  ;::: IM & Info Errors
  elseif ($1 == 960) aim.API_error You are sending message too fast to $2-
  elseif ($1 == 961) aim.API_error You missed an im from $2 because it was too big.
  elseif ($1 == 962) aim.API_error You missed an im from $2 because it was sent too fast.
  ;::: Auth errors
  elseif ($1 == 980) aim.API_error Incorrect nickname or password.
  elseif ($1 == 981) aim.API_error The service is temporarily unavailable.
  elseif ($1 == 982) aim.API_error Your warning level is currently too high to sign on.
  elseif ($1 == 983) aim.API_error You have been connecting and disconnecting too frequently.  Wait 10 minutes and try again. If you continue to try, you will need to wait even longer.
  elseif ($1 == 989) aim.API_error An unknown signon error has occurred $2-
}

;; =========================================================
;; Auxiliary aliases

;; Function: retrieves data about the connection
;; Usage: $aim_get(<query>)
alias aim_get {
  if (!$hget(aim)) return
  return $hget(aim,$$1)
}

;; Function: adds data about the connection
;; Usage: /aim.add <item> <value>
alias aim.add { hadd -m aim $$1 $2 }

;; Function: parses a binvar for FLAP information
;; Usage: $aim_flap(<&binvar>)[.type|.number|.len]
;; Comments: FLAP header is 6 bytes long, data follows
alias aim_flap {
  ;:::  literal asterisk (*) takes up the first byte (1 byte)
  ;:::  frame type (1 byte)
  if ($prop == type) return $bvar($1,2)
  ;:::  sequence number (2 bytes)
  elseif ($prop == number) return $aim_fromInt16($bvar($1,3,2))
  ;:::  data length (2 bytes)
  elseif ($prop == len) return $aim_fromInt16($bvar($1,5,2))
}

;; Function: creates a flap header based on a supplied binvar's information
;; Usage: $aim_buildflap(<frame type>,<binvar holding data>)[.null]
;; Comments: the .null property will add a null character to terminate string
alias aim_buildflap {
  bunset &flap
  ;::: increase the sequence number for the next transmission
  aim.add sequence_number $calc($aim_get(sequence_number) + 1)
  ;::: FLAP header anatomy: [*] [frame type (1 byte)] [sequence number (2 bytes)] [data length (2 bytes)]
  bset -t &flap 1 *
  bset &flap 2 $1
  bset &flap 3 $aim_toInt16($aim_get(sequence_number))
  bset &flap 5 $aim_toInt16($bvar($2,0))
  ;::: add header to data binvar
  bcopy &flap 7 $2 1 -1
  ;::: null terminate if required - also recompute data length value to account for null terminator
  if ($prop == null) {
    bset &flap $calc($bvar(&flap,0) + 1) 0
    bset &flap 5 $aim_toInt16($calc($bvar($2,0) + 1))
  }
  return &flap
}

;; Function: 'Roasts' a password for use with AIM
;; Usage: $aim_roast(<password>)
;; Comments: Roasting is simply $xor'ing each character of the password with the corresponding
;;           character of the roasting string - the roasting string for AIM TOC is 'Tic/Toc'
alias aim_roast {
  ;:::  initialize variables for loop - roasting pass starts with 0x like any hex representation
  var %roast = Tic/Toc, %roast_len = 7, %roasted_pass = 0x, %i = 1
  ;:::  loop through each character of the password supplied
  while ($asc($mid($1,%i,1))) {
    var %char = $ifmatch
    ;::: grab the ASCII value of the corresponding char from the roasting string
    var %roast_char = $asc($mid(%roast,$iif($calc(%i % %roast_len), $ifmatch, %roast_len),1))
    ;:::  XOR the char from the pass from the char from the roasting string
    var %xor_char = $xor(%char,%roast_char)
    ;:::  get the hexadecimal value of the XOR'd char
    var %hex_char = $base(%xor_char,10,16,2)
    ;:::  concatenate the roasted password
    %roasted_pass = %roasted_pass $+ %hex_char
    inc %i
  }
  ;::: return the lowercase of the roasted password when completed
  return $lower(%roasted_pass)
}

;; Function: "normalizes" a username - removes the spaces and makes lowercase
;; Usage: $aim_normalize(<username>)
alias aim_normalize {
  var %username = $remove($1,$chr(32))
  return $lower(%username)
}

;; Function: converts from INT16 (2 bytes to 1 byte)
;; Usage: $aim_fromInt16(1 44)
alias aim_fromInt16 { return $calc($gettok($1,1,32) * 256 + $gettok($1,2,32)) }

;; Function: converts to INT16 (1 byte to 2 bytes)
;; Usage: $aim_toInt16(300)
alias aim_toInt16 { return $int($calc($1 / 256))  $calc($1 % 256) }

;; Function: sends a command to get a users info
;; Usage: /aim.getinfo <nickname>
;; Comments: Used in combination with aim.API_handleinfo, which receives the reply from your request
alias aim.getinfo {
  aim.out toc_get_info $1
}

;; Function: sets an away message
;; Usage: /aim.away <message>
alias aim.away {
  aim.out toc_set_away $+(",$1-,")
}

;; Function: sets an away message
;; Usage: /aim.away <message>
alias aim.back {
  aim.out toc_set_away
}

;; Function: sends a message to a user
;; Usage: /aim.message <username> <message>
alias aim.message {
  aim.out toc_send_im $1 $+(",$2-,")
}

;; Function: warns a user
;; Usage: /aim.warn <username> <anonymous? $true|false>
alias aim.warn {
  aim.out toc_evil $$1 $iif($2,"anon","norm")
}

;; Function: reset idle time
;; Usage: /aim.idle
alias aim.idle {
  aim.out toc_set_idle 0
}

;; Function: change password
;; Usage: /aim.changepassword <old password> <new password>
alias aim.changepassword {
  aim.out toc_change_passwd $1 $2
}

;; Function: change nickname formatting
;; Usage: /aim.nick <nickname format>
alias aim.nick {
  aim.out toc_format_nickname $1-
}


This is an example script that should integrate with the core protocol above - under construction



alias test {
  if (%test) {
    echo 2 -s *** Test not ready for reconnect
    .timer 1 %test test
    return
  }
  inc -z %test 180
  aim.connect <username> <password>
}
;; =========================================================
;; GUI section

;; Function: opens up the aim window if not already open
;; Usage: /aim
alias aim {
  if (!$window(@aim)) window -el50 @aim
}

;; Function: handles the response for a getinfo request
;; Usage: /aim.API_getinfo <&binvar holding data>
alias aim.API_getinfo {
  ;::: find the username for this getinfo data
  var %start = $calc($bfind($1,1,<TITLE>User Information for).text + 28)
  var %end = $calc($bfind($1,1,</TITLE>).text - %start)
  var %username = $bvar($1,%start,%end).text
  var %window = $+(@profile-,$aim_normalize(%username))
  if (!$window(%window)) {
    ;::: create the window
    window -Cpdf +b %window 0 0 250 300
    ;::: create the bgcolour
    drawfill -nrs %window $rgb(face) $color(0) 1 1
    ;::: create the titlebar
    drawtitle -n %window $rgb(face) $rgb(200,200,200) arial 10 $rgb(255,0,0) %username
  }
  ;::: find where the first segment (the user information) ends - ends at next <HTML> tag oddly enough
  var %info_end = $calc($bfind($1,7,<HTML>).text - 1)
  ;::: create binvar to hold user information
  bcopy &info 1 $1 1 %info_end
  ;::: find where the second segment (the user profile) ends
  var %profile_end = $calc($bfind($1,%info_end,</HTML>).text - %info_end + 6)
  ;::: create binvar to hold profile
  bcopy &profile 1 $1 $calc(%info_end + 1) %profile_end
  aim.displayhtml %window 5 75 240 220 2 arial 10 &profile
}


;; Function: display the HTML info in a specified block of a specified picwin
;; Usage: /aim.displayhtml <window> <x> <y> <w> <h> <padding> <font name> <font size> <&binvar>
;; Comments: x y w h are dimensions to the block you want it to write the data to, font params are
;;           for the default font
alias aim.displayhtml {
  ;::: assign the parameters to variables
  var %i = 1, %params = window x y w h padding fname fsize data
  while ($gettok(%params,%i,32)) { set -l % [ $+ [ $ifmatch ] ] $gettok($1-,%i,32) | inc %i }
  var %line_spaces
  ;::: loop until there's no more tags to find
  while ($bfind(%data,1,<).text) {
    ;::: find the start and end position of the next tag (< and >)
    var %start = $ifmatch, %end = $bfind(%data,1,>).text
    ;::: get the next HTML tag
    var %tag = $bvar(%data,%start,$calc(%end - %start + 1)).text
    ;::: get any text preceeding this tag and remove HTML tags from it
    var %text = $nohtml($bvar(%data,1,$calc(%start - 1)).text)
    ;::: display the text
    if (%text) {
      ;::: derive the RGB value from the HEX colour value - if it's a link, change colour accordingly
      var %drawColour = $rgb( [ [ $rgbhtml($iif(%colour,%colour,$iif(%linkcolour,%linkcolour,#000000))) ] ] )
      ;::: get RGB value of the bg colour of the text - if not bg colour specified for text, use body bgcolour
      var %drawBGColour = $rgb( [ [  $rgbhtml($iif(%bgcolour,%bgcolour,%bodybgcolour)) ] ] )
      ;::: adjust font properties or go w/ defaults
      var %drawsize = $calc(%fsize + %size * 2), %drawFace = $iif(%face,$+(",%face,"),%fname)
      ;::: draw the text and wrap it (also center it if neccessary)
      drawwrap -nbrp $+ $iif(%center,x) %window %drawColour %drawBGColour %drawFace %drawSize %x $calc(%y + %line_spaces) %w $calc(%h - %line_spaces) %text
      ;:: if text text needs to wrap, add more $crlf's to correct for wrapping
      if ($wrap(%text,%drawFace,%drawSize,%w,1,0) > 1) inc %line_spaces $calc(%drawSize * $ifmatch)
    }
    if (<BODY BGCOLOR=*> iswm %tag) {
      ;::: get background colour for profile
      var %bodybgcolour = $remove($gettok($gettok(%tag,2,61),1,62),")
      ;::: draw the display area
      drawbox -fn %window $rgb( [ [ $rgbhtml(%bodybgcolour) ] ] ) %x %y %w %h 0
      ;::: create a padding around the display area
      var %x = $calc(%x + %padding), %y = $calc(%y + %padding), %w = $calc(%w - %padding * 2), %h = $calc(%h - %padding * 2)
    }
    elseif (<FONT *> iswm %tag) {
      ;::: get font properties for the next text found
      var %regex = $regex(colour,%tag,m~COLOR="([^"]+)"~Sig), %colour = $regml(colour,1)
      var %regex = $regex(bgcolour,%tag,m~BACK="([^"]+)"~Sig), %bgcolour = $regml(bgcolour,1)
      var %regex = $regex(face,%tag,m~FACE="([^"]+)"~Sig), %face = $regml(face,1)
      var %regex = $regex(size,%tag,m~SIZE=(\d+)~Sig), %size = $regml(size,1)
      echo 2 -s %tag -- %colour - %bgcolour - %bodybgcolour
    }
    ;::: destroy the current font settings
    elseif (%tag == </FONT>) var %colour, %bgcolour, %face, %size
    ;::: increase the vertical space between the last text and the next text
    elseif (%tag == <BR>) inc %line_spaces $calc(%fsize + %size * 2)
    ;::: change the font colour if a link is found
    elseif (<A HREF=*> iswm %tag) var %linkcolour = #0000FF
    ;::: unset the font colour for link once link ends
    elseif (%tag == </A>) var %linkcolour
    ;::: make the text center/uncentered
    elseif (%tag == <center>) var %center = $true
    elseif (%tag == </center>) var %center = $false
    if ($calc(%end + 1) > $bvar(%data,0)) break
    ;::: crop out the current segment
    bcopy -c %data 1 %data $calc(%end + 1)  -1
  }
  ;::: finalize the drawing
  drawdot %window
}

menu @profile-* {
  sclick: {
    if ($inrect($mouse.x,$mouse.y,0,0,$window($active).w,17)) movew $active $mouse.x $mouse.y
  }
  Close: window -c $active
}

;; =========================================================
;; API section

;; Function: creates a buddy list; stores the buddy names
;; Usage: /aim.API_createbuddylist <item type> <item name>
alias aim.API_createbuddylist {
  var %item_type = $1, %item_name = $2
  ;::: if item is a group, make it the current group
  if (%item_type == g) aim.add current_group %item_name
  ;::: if item is a buddy, add it to the buddy list
  elseif (%item_type == b) aim.add $+(buddy_,$aim_get(current_group),_,%item_name)
}

;; Function: updates the buddy list with the current user information
;; Usage: /aim.API_updatebuddy <name> <online> <warn> <sign_on> <idle> <unconfirmed> <unavailable> <on AOL> <connection type> <away: $true|$false>
alias -l aim.API_updatebuddy {
  tokenize 58 $1-
  var %name = $1, %online = $2, %warn = $3, %sign_on = $4, %idle = $5, %away = $6, %internet = $7
}

;; Function: handles an error message
;; Usage: /aim.API_error <message>
alias aim.API_error {
  echo 5 -s Error: $1-
}

;; Function: handles an incoming message
;; Usage: /aim.API_incomingmessage <username>:<automated response [T|F]>:<&binvar holding message>
alias aim.API_incomingmessage {
  tokenize 58 $1-
  echo 4 -s $1 -- $2 -- $bvar($3,1,$bvar($3,0)).text
}

;; Function: handles the event when you get warned by another user
;; Usage: /aim.API_warn <warn level> <username>
alias -l aim.API_warn {
  echo 4 -s you have been warned: $1 $+ % warning level
}

;; Function: handles the debug information
;; Usage: /aim.API_debug <description>:<data>
alias aim.API_debug {
  tokenize 58 $1-
  echo 4 -s $+($chr(2),$1,$chr(2),:) $2-
}

;; =========================================================
;; Auxiliary aliases

alias readbytes {
  var %i = 1, %string
  while ($gettok($1-,%i,32)) {
    %string = %string $+ $chr($ifmatch)
    inc %i
  }
  return %string
}

;; Function: converts RGB to HEX and vica versa
;; Usage: $rgbhtml(<RGB|HEX>)
;; Comments: "rgb <-> html" by cold @ http://mirc.is.dreaming.org
alias rgbhtml {
  if (!$1) return
  if (#* !iswm $1) {
    tokenize 44 $iif($chr(44) isin $1-, $remove($1-, $chr(32)), $replace($1-, $chr(32), $chr(44)))
    var %in = $replace($1-, $chr(32), $chr(44)), %out = $+($chr(35), $base($1, 10, 16, 2), $base($2, 10, 16, 2), $base($3, 10, 16, 2))
  }
  else {
    tokenize 32 $mid($1, 2, 2) $mid($1, 4, 2) $mid($1, 6, 2)
    var %in = $remove($+($chr(35), $1-), $chr(32)), %out = $+($base($1, 16, 10, 0), $chr(44), $base($2, 16, 10, 0), $chr(44), $base($3, 16, 10, 0))
  }
  return %out
}

;; =========================================================
;; Picwin construction aliases

;; Function: Draws wrapped text and optionally centers it in a specified block
;; Usage: /drawwrap [-xybrnp] <window> <colour> <bgcolour> <fontname> <fontsize> <x> <y> <w> <h> <text>
;; Comments: -x and -y params are used to center text horizontally and vertically respectively
alias drawwrap {
  ; parse parameters
  var %params = $iif(-* iswm $1,switches) window colour $iif(-*b* iswm $1,bgcolour) fname fsize x y w h
  var %regex = $regex(params,$1-,m~("[^"]+")|([^\s]+)~iSxg), %text, %i = 1
  while ($gettok(%params,%i,32)) { set -l % [ $+ [ $ifmatch ] ] $regml(params,%i) | inc %i }
  ; determine which parameters are text input params
  while ($regml(params,%i)) { %text = %text $ifmatch | inc %i }
  ; loop through the wrapped lines
  var %i = 1
  while ($wrap(%text,%fname,%fsize,%w,1,%i)) {
    var %wrappedtext = $ifmatch
    var %text_w = $width(%wrappedtext,%fname,%fsize)
    var %text_h = $height(%wrappedtext,%fname,%fsize)
    ; adjust for horizontal centering (if specified with -x switch)
    if (x isletter %switches) var %text_x = $int( $calc( (%w - %text_w) / 2 + %x) )
    else var %text_x = %x
    ; find the offset to adjust for Y space in order to center block of textif (w isletter switches)
    if (y isletter %switches) var %center_y = $calc((%h - $wrap(%text,%fname,%fsize,%w,1,0) * %text_h) / 2)
    ; if the offset is > 0 (the block height < specified height) add the centering offset factor
    var %text_y = $calc($iif(%center_y > 0,%center_y) + %text_h * (%i - 1) + %y)
    ; bound the block of text by the specified height
    if ($calc(%text_y + %text_h) > $calc(%h + %y)) break
    ; draw the bloody text
    drawtext $+(-,$remove(%switches,-),c) %window %colour %bgcolour %fname %fsize %text_x %text_y %w $height(%wrappedtext,%fname,%fsize) %wrappedtext
    inc %i
  }
}

;; vertical scroll drawtext
; Usage: /vsdrawtext -porhn @window <color> <color> [fontname fontsize] <x> <y> <w> <h> <text>
; x y w h are all values of the text area
alias vsdrawtext {
  var %switches = $iif(-* iswm $1,1), %i = 1
  while ($gettok(window colour bgcolour font fontsize x y w h,%i,32)) { set -l % [ $+ [ $ifmatch ] ] $gettok($1-,$calc(%switches + %i),32) | inc %i }
  var %text = $iif(%switches,$11-,$10-), %textheight = $height(%text,%font,%fontsize)
  if (!$window(%window)) { echo $color(info) -s * /vsdrawtext: invalid window | return }
  elseif (!%text) { echo $color(info) -s * /vsdrawtext: invalid parameters | return }
  drawscroll $iif(-*n* iswm $1,-n) %window 0 $+(-,%textheight) %x $calc(%y + %textheight) %w %h
  drawrect -fr %window %bgcolour 0 %x $calc(%y + %h - %textheight) %w %textheight
  drawtext -bc $+ $remove($iif(%window == $2,$1),-) %window %colour %bgcolour %font %fontsize %x $calc(%y + %h - %textheight) %w %textheight %text
}


;; Draws a titlebar onto top of window
; usage: /drawtitle -n <@window> <bgcolour> <title colour> <font name> <font size> <font colour> <text>
; <bgcolour> is the background colour, <title colour> is the colour of the background for the text
alias drawtitle {
  var %i = 1, %params = $iif(-* iswm $1,switches) window bgcolour colour fname fsize fcolour
  while ($gettok(%params,%i,32)) { set -l % [ $+ [ $ifmatch ] ] $gettok($1-,%i,32) | inc %i }
  var %text = $iif(%switches,$8-,$7-)
  ; colour the background of the titlebar
  drawbox -nf %window $rgb(120,120,120) 0 1 $calc($window(%window).w - 3) 16 1
  drawbox -n %window 3 2 $calc($window(%window).w - 9) 3 0
  drawbox -n %window 3 7 $calc($window(%window).w - 9) 3 0
  drawbox -n %window 3 12 $calc($window(%window).w - 9) 3 0
  drawbox -nf %window %colour $calc(($window(%window).w / 2) - 50) 3 100 12 0
  drawtext -n %window 0 %fname %fsize $calc(($window(%window).w / 2) - 25) 2 %text
  if (n !isin %switches) drawdot %window
}

;; draws a box
; usage: /drawbox -nf <window> [colour(RGB)] <x> <y> <w> <h> <1|0>
; the last parameter determines whether box is raised (1) or lowered (0)
alias drawbox {
  ; parse parameters
  var %i = 1, %params = $iif(-* iswm $1,switches) window $iif(-*f* iswm $1,colour) x y w h R
  while ($gettok(%params,%i,32)) { set -l % [ $+ [ $ifmatch ] ] $gettok($1-,%i,32) | inc %i }
  drawline %switches %window $iif(%R,0,1) 1 %x %y $calc(%x + %w) %y
  drawline %switches %window $iif(%R,0,1) 1 %x %y %x $calc(%y + %h)
  drawline %switches %window $iif(%R,1,0) 1 $calc(%x + %w) %y $calc(%x + %w) $calc(%y + %h)
  drawline %switches %window $iif(%R,1,0) 1 %x $calc(%y + %h) $calc(%x + %w + 1) $calc(%y + %h)
  if (%colour) drawrect %switches $+ r %window %colour 0 $calc(%x + 1) $calc(%y + 1) $calc(%w - 1) $calc(%h - 1)
}

;; draws a button
; usage: /drawbutton -n <window> <raised> <colour(RGB)> <x> <y> <w> <h> <font name> <font size> <font colour> <command>:<text>
; <raised>, 1 for up, 0 for down
alias drawbutton {
  ; parse parameters
  var %i = 1, %params = $iif(-* iswm $1,switches) window state colour x y w h fname fsize fcolour
  while ($gettok(%params,%i,32)) { set -l % [ $+ [ $ifmatch ] ] $gettok($1-,%i,32) | inc %i }
  var %command = $gettok($gettok($1-,$iif(%switches,12-,11-),32),1,58), %text = $gettok($gettok($1-,$iif(%switches,12-,11-),32),2,58)
  var %width = $width(%text,%fname,%fsize), %height = $height(%text,%fname,%fsize), %center_x = $calc(%x + (%w - %width) / 2)
  ; draw button
  drawbox -nf %window %colour %x %y %w %h %state
  drawtext -cnr %window %fcolour %fname %fsize $iif(%center_x < %x,%x,%center_x) $calc(%y + (%h - %height) / 2) %w %height %text
  if (n !isin %switches) drawdot %window
  ; add button info to hash table
  hadd -m %window button. $+ $calc($hfind($active,button.*,0,w) + 1) %x %y %w %h %state %command
}

;; Function: Drags active window
;; Usage: /movew <@window> <x> <y>
;; Comments: from ^Andy at ms.org
alias movew {
  if ($mouse.key & 1) && ($window($1)) {
    window $1 $calc($mouse.dx - $2) $calc($mouse.dy - $3)
    .timermove_ $+ $1 -o 1 0 movew $1-
  }
}

very sexy para
Posted by variant
Friday, July 23, 2004 11:14pm PDT
glad to see you're getting back into the scripting scene :D
working
Posted by Scoobster
Saturday, July 24, 2004 03:47am PDT
how is it working para i put it in remotes but how i get it to work im confused
advice.. again.
Posted by variant
Saturday, July 24, 2004 05:55am PDT
atleast have a little idea about what you're doing before you try to use or make something semi complicated.
keep in mind
Posted by Parasite-FT-
Saturday, July 24, 2004 10:44am PDT
keep in mind that this is an ongoing project, and shall be updated regularly - the above snippet was *just* updated to login all sexy like. just type /test after changing your username and password and you'll notice that you have logged in (by viewing your nick on some other AIM account)

- Para
update
Posted by Parasite-FT-
Tuesday, July 27, 2004 01:45am PDT
working on a sample GUI for people; dunno how well picwins will go over, but damn it, picwins is what you'll get. Maybe i'll also add in a GUI made for mAIM if I get authorization to do so by b00hp and codemastr.

I added the ability to get profiles and display it into a picwin. parsing the HTML and converting it into picwin commands was a real bitch, and the code isn't pretty looking, but hopefully i can fix it up nicely in the future.
omg
Posted by m4rine
Thursday, July 29, 2004 12:16pm PDT
your scripting but not in #script, thats not allowed.  You can't script anymore till you idle in script ALOT and talk and be sexy.
lol
Posted by Parasite-FT-
Thursday, July 29, 2004 08:22pm PDT
<3
Hmm
Posted by knightrage
Friday, July 30, 2004 04:36am PDT
Just two comments.. What's the point of using a $portfree(%port) check (line 14, in /aim.connect alias ) to make sure the port that you're connecting to isn't in use on your computer?

And, the comment you made "... TOC protocol which is a level above OSCAR." Maybe I'm interpreting it wrong, but isn't OSCAR above TOC? AFAIK, TOC only supports instant messages and chatting.
yes
Posted by Parasite-FT-
Saturday, July 31, 2004 06:37am PDT
to the first question, it's to find an available port on the client end - the server allows any port to be used so I thought I'd cycle through some ports on the client end in case they're being used by something else. Kinda silly, but I encountered the problem while another script was using a specified port so I decided to add a workaround.

as to the second, TOC is one level above OSCAR in that it's simply a proxy for the OSCAR protocol. It interprets your commands and sends them to the OSCAR protocol; thankfully that simplifies commands while maintaining enough functions to make it useful. You can get away messages, set idle time, set/get profile information, chat, IM, etc, so while it loses some functionality (p2p connections, file transfers, etc) it still maintains enough usability to be worthy of attempting.
Submit a comment
Oops! You need to login or register before you can post a comment!

ebaum's world