Stockticker with e-mail notification

This is a stand-alone or application add-on. It requires either the Spynergy Toolkit or the HTTP package.





# ------------------------------------------------------
# Stockticker application that notifies user with e-mail
# when a stock price is above or below a specified range.
# Uses fetchURL or HTTP package
#
#  Copyright (c) 1997 Eolas Technologies Inc. and Clif Flynt
#  Freely modifiable/redistributable under the "Standard Tcl License"
#  See http://www.eolas.com/tcl/license.txt for details
# ------------------------------------------------------


# ------------------------------------------------------
# Requiring outside resources
# ------------------------------------------------------


catch {package require Safesock}
catch {package require http}
catch {source spynergy.tcl}

# ------------------------------------------------------
# Ticker procedure
# ------------------------------------------------------


proc doticker {textWindow delay text width} {
    global index messages header once

    # Find the location of the last character on the second line
    # and if that is less than the possible last character,
    # add a new set of strings to the scrolling text in the window.

    scan [$textWindow index 2.end] %d.%d line len

    if {$len < $width} {
	if {![info exists messages($index)]} {
	    set index 0
	}
	set message $messages($index)

	$textWindow configure -state normal
	$textWindow insert 1.end "$text" fill
	$textWindow insert 1.end $header

	# Need a \n at the end of the line 1 to insert on line 2.

	if {$len == 0} {
	    $textWindow insert 1.end "\n" 
	  }

	$textWindow insert 2.end "$text" fill
	$textWindow insert 2.end $message tag$index
	$textWindow configure -state disabled

	incr index
    }

    # Delete the first characters in the first and second line,
    # to scroll the text one position to the left.

    $textWindow configure -state normal
    $textWindow delete 2.0
    $textWindow delete 1.0
    $textWindow configure -state disabled
    after $delay [list doticker $textWindow $delay $text $width]
}


# ------------------------------------------------------
# check_stocks: sends e-mail to user when a stock price is 
# above or below specified range.
# ------------------------------------------------------


proc check_stocks { symbol price company } { 

    global stock_list 

    set stock_list [list aapl gwrx ibm mot msft nscp pkt spyg sunw]

    array set range {
	aapl {15 20}
	gwrx {5 30}
	ibm {5 120}
	mot {25 100} 
	msft {25 200}
	nscp {20 150}
	pkt {20 80}
	spyg {6 20}
	sunw {25 75}
	}  



scan "$price" %d last

set start [lindex $range($symbol) 0] 
set end [lindex $range($symbol) 1] 

set pricelist [split "$price"]
set wholeprice [lindex $pricelist 0]
set fraction " "
catch "set fraction [lindex $pricelist 1]"  


set companylist [split "$company"]
set compfirst [lindex $companylist 0]
set compsec " "
catch "set compsec [lindex $companylist 1]"  

# ------------------------------------------------------
# Configure the name and e-mail variables below to the 
# desired name and e-mail
# ------------------------------------------------------

set name John+Doe
set email john@doe.com


if { $last < $start } {   

   
   if {[catch "::http::geturl \
http://www.neosoft.com/neowebscript/demos/emailscript.html?name=$name&recipient=$email&email=$email&comments=The+$compfirst+$compsec+stock+is+down%0D%0Alast+price+is+$wholeprice+$fraction%0D%0A"]} {

fetchURL  \
 http://www.neosoft.com/neowebscript/demos/emailscript.html?name=$name&recipient=$email&email=$email&comments=The+$compfirst+$compsec+stock+is+down%0D%0Alast+price+is+$wholeprice+$fraction%0D%0A
        }      
  } elseif  { $last > $end } { 
   
   if {[catch "::http::geturl \
http://www.neosoft.com/neowebscript/demos/emailscript.html?name=$name&recipient=$email&email=$email&comments=The+$compfirst+$compsec+stock+is+up%0D%0Alast+price+is+$wholeprice+$fraction%0D%0A"]} {

 fetchURL \
http://www.neosoft.com/neowebscript/demos/emailscript.html?name=$name&recipient=$email&email=$email&comments=The+$compfirst+$compsec+stock+is+up%0D%0Alast+price+is+$wholeprice+$fraction%0D%0A
       }
 } else { 
}
}


;# reformTable {lst}
;# Takes a list composed of lines (one line per list item) as returned
;#   by a webserver, and returns a list composed of one Table Row per
;#   list entry.
;#  ie:
;#  input:
;#    
;#      one two
;#    
;#  output:
;#   one two 

proc reformTable {lst} {
      
    if {[string length $lst] < 5} {return ""}
    set tdState 0
    set tdline ""
    
    #
    # Loop through the list items checking for Table markers ( 
    # Append the text between  and  (inclusive) into a single
    #   list item in a new list.
    #
    # The automata is in one of these states:
    # 
    #  0) No  2} {
		    if {([string first "= 0} {
		set st2 [expr $st1+4]
		set tdline [string range $line $st1 $st2]
		set line [string range $line $st2 end]
		set tdState 1
	    }

	    # If there is a  should be transfered to the holding space,
	    # before the holding string is lappended to the list
	    # of table definitions.

	    if {[set st1 [string first "= 0} {
		set st2 [expr $st1+5]
		if {$tdState} {
		    append tdline [string range $line 0 $st2]
		    lappend tdlst $tdline
		    set tdline ""
		    set tdState 0
		}
		set line [string range $line $st2 end]
	    }
	}
    }
    return $tdlst
}

proc extractNumber {line} {
    # The numeric values are extracted using a regular expression that
    # looks for a string of numbers between a ">", and a "<"

    set xx [regexp {>([	 ]*)([0-9%\./-]+ *[0-9%\./-]*)([ 	]*)<} $line \
      mat p1 p2 p3]
    if {$xx == 0} {
      return "XX"
      } else {
      return $p2
      }
  }



proc get_data { id } {
  global messages formatstr stock_list state
 set stock_list [list aapl gwrx ibm mot msft nscp pkt spyg sunw]
upvar #0 $id state
set symbol [string range $state(url) 47 end]
set upsym [string toupper $symbol]

set data $state(body)

format_data $symbol $data $stock_list      

   }

#======================================================================
#::  displayStocks {stock_list win} 
#: displays a list of stocks in the appropriate window.
#: Initializes the message list for the display
#:
#: stock_list	- A list of the ticker symbols of stocks to display
#: win		- The window in which to display the stock readings.

proc displayStocks {stock_list win} {
  global messages header index formatstr 
   
   foreach symbol $stock_list {


  if {[catch "::http::geturl \
http://www.newsalert.com/free/stocknews?Symbol=$symbol \
	-command get_data"]} {
  
# ----------------------------------------------------
# Retrieve the data with fetchURL if the Http package 
# is not available. 
# ----------------------------------------------------
  source spynergy.tcl
  set data [fetchURL  \
http://www.newsalert.com/free/stocknews?Symbol=$symbol]

# -----------------------------------------------------
# Call the format_data procedure to format and place 
# the data (only called if the procedure is using the 
# Spynergy Toolkit)  
# ------------------------------------------------------

 format_data $symbol $data $stock_list       
    }
     }
 after 900000 "displayStocks [list $stock_list] $win"
}



proc format_data { symbol data stock_list} {

global messages formatstr state


# Extract the table with the stock values from the output.
    # This is the portion of the data between Symbol=XXX and the end of the
    # table.

set upsym [string toupper $symbol]

    set st1 [string first "Symbol=$symbol" $data]
    set st2 [string first "" $data]
    incr st1
    set data [string range $data $st1 $st2]

    # Skip the first two rows in the table.

    set st1 [string first "" $line]
    set line [string range $line $st1 end]
    set st1 [string first ">" $line]
    set st2 [string first "<" $line]
    incr st1
    incr st2 -1

    #  puts "st1: $st1 st2: $st2 line: $line"
    set company [string range $line $st1 $st2]"

    # The numeric values are extracted using a regular expression that
    # looks for a string of numbers between a ">", and a "<"

    set last [extractNumber [lindex $tdlst 3]]
    set chg  [extractNumber [lindex $tdlst 4]]
    set pct  [extractNumber [lindex $tdlst 5]]
    set open [extractNumber [lindex $tdlst 7]]
    set high [extractNumber [lindex $tdlst 8]]
    set low  [extractNumber [lindex $tdlst 9]]

  set stockinfo [format $formatstr \
      $upsym [string range $company 0 11] $last $chg $pct $open $high $low]



    if {$stockinfo != ""} {

         set in  [expr [lsearch -exact $stock_list $symbol]]

         set messages($in) "$stockinfo"
	   
       if {[info exists messages([expr [llength $stock_list] -1])]} {
    	doticker .f.t 1000 "  ******  " 250
      }

      #comment out the following line if you don't want the stock data 
      #  echo'ed to the console
       puts $messages($in)

     check_stocks $symbol "$last" "$company"
}
}

#======================================================================
#:: init_ticker {win} 
#:  initialize the stock ticker.  Creates the window and loads the stocks.
#:  win		- Parent window for the text window.

proc init_ticker {win} {

  # Define the globals that the stock subsystem uses, and initialize them.

  global index messages header formatstr
 
  set index 0; set messages(0) ""; set header "";

  set formatstr "%-10s %-12s %10s %10s %12s %12s %12s %12s"

  set header [format $formatstr \
  	Symbol "Company Name" Lst Chg Pct Open High Low ]

  text $win.t -relief ridge -bd 2 -wrap none \
            -bg black -fg green -state disabled -height 2
  pack $win.t 

  set stock_list [list aapl gwrx ibm mot msft nscp pkt spyg sunw]

  displayStocks "$stock_list" $win.t
}


frame .f -height 10
pack .f -anchor nw -side top -expand 1 -fill x
init_ticker .f