- Joined
- Oct 8, 2001
- Location
- Redmond, WA
Windows setup
Thanks to BeerCan for writing this section up.
Ok. Thanks to Christoph and his new perl script you can now update your signatures from a windows machine automatically.
There is one prerequisite. You need to download a win32 version of perl. You can find it here http://www.activestate.com/Products/Download/Download.plex?id=ActivePerl. I downloaded the msi version but your preference may vary. The AS Package doesn't provide any uninstall functionality, and ActiveState recommends it only if you can't use the MSI installer. Install using all of the defaults. It will create a directory c:\Perl and automatically install all necessary files there. It will also create your program groups and associate all perl extensions with perl.exe. The files we care about will end with the .pl extension. Next, we need a copy of Christoph's mksig script.
Create a directory on your c:\ drive called c:\mksig. Then, save the above linked file as c:\mksig\mksig.pl . Please make sure it has just the .pl extension and not the .pl.txt extension. If you renamed the file to mksig.pl and it still has the text icon, you need to disable hiding of known extenstions. If you do it right, the icon should turn from the notepad icon to an icon with a lizard on it. Next, execute mksig.pl. Either double click the icon or run it from a dos prompt. Both methods work. Answer all of the questions. It should create the following files in c:\mksig -- mksig.conf, sig, sig_gen.pl and sig.log. These are explaned later in this howto.
If all of those files are there, it installed correctly.
The next step is to make sure that any firewalls you're running don't block Perl.exe from connecting to the Internet. How this is accomplished will depend on your firewall, so check its documentation for more information. If you get an error like Please make sure that Perl can connect to the outside world. , you probably forgot to unblock Perl.exe.
If you want to keep your current forum sig, you should save it now, since mksig doesn't currently back up old sigs. Now when you run mksig.pl again it will update your sig Now we need to get it running as a task so it will update automatically.
Open the windows Scheduled Tasks manager. On windows XP it's start-->All Programs-->Accessories-->System Tools-->Scheduled Tasks on Win2K its Start-->Programs-->Accessories-->System Tools-->Scheduled Tasks.
Next click Add a Scheduled Task-->Next on the next screen you are going to hit Browse and browse to C:\mksig\mksig.pl and click open. Next you need to pick how often to run the task. I picked daily. Next, customize the start time to your liking and click next. On the next screen you need to enter the windows user that you would like to run the task under. This is what you use to login to windows and has nothing to do with your forum username. Make sure you enter a password or the task won't run properly. Click next. If you would like to run the task more often than daily, click on the open advanced properties checkbox and click finish. Now you can click the schedule tab and customize to your liking. It should be clear. Click ok and save and you are done.
Of course, if you want to change your sig you need to customize sig_gen.pl. That's covered in the last section of this howto.
Linux setup
Most distros today have Perl available, and installing it is usually a simple matter. For this script, you'll need Perl, LWP (which comes with the base Perl system), the Digest::MD5 module and the XML::Simple module if you're running Linux. If Digest::MD5 isn't installed on your system, just run the following command as root: perl -MCPAN -e "install Digest::MD5" or install from a distro-specific package. If you've never used CPAN before, you'll be asked a number of questions about your setup, and the overall setup can take several minutes. If you're running Gentoo, Debian or their offspring, you can install Digest-MD5 and XML-Simple or libdigest-md5-perl and libxml-simple-perl, respectively and avoid the hassle of CPAN. I don't know if similar packages are available for other distros.
Once you've got Digest::MD5 and XML::Simple installed, download mksig. To get it ready to run, do the following commands as root:
The previous instructions recommended changing the ownership to root:root. With versions .62 and later, having mksig owned by either root or non-root will work, although installing as root and running as non-root will prevent automatic updates form working. Since you should *never ever* run day-to-day stuff like this as root, I recommend leaving the default ownership.
Now run mksig as a normal user. Tell it your username and password when asked and use y or n to indicate if you want automatic updates. (If you enable this, mksig will check for a newer version of itself each time it runs, and if possible will download, install and run it. This feature has been tested in both windows and Linux.) Now you're ready to use the default sig. Note that mksig won't change your sig the first time it's run. This is to give you a chance to backup the old one and edit your sig generator (see next section). All user-specific information is stored in ~/.mksig and is covered in the next section.
To get mksig to run at regular intervals, you have a couple options.
The simplest is a backgrounded while loop. Pick and update interval and run (while let 1 do; mksig >/dev/null; sleep 30m; done) & and your sig will silently be updated every half-hour. If you need it to stop updating, simply run pkill sleep and it'll stop.
A similar method is to the first is while loop in the foreground. Simply run while let 1 do; mksig >/dev/null; cat ~/.mksig/sig; sleep 30m; done and your sig will be updated every 30 minutes, and the vbcode will be displayed in the terminal window.
The third method is to use a cron job. cron jobs are executed at regular intervals and are quite simple to set up. You can find Gentoo's instructions on three popular cron implementations here.
What the Files Do
There are four files: two config files and two logs. In mksig's directory, mksig.conf contains your username and password. sig_gen.pl is the Perl script (or any executable) whose output is submitted as your sig. Under Linux this file is called sig_gen because it's easier to type and Linux doesn't rely on filename extensions. sig contains the most recently submitted sig, and sig.log contains every sig mksig has submitted, along with a timestamp.
How to Get Your DC Stats in Your Sig
mksig determines what your sig will look by executing mksig.pl . Whatever it spits out will be your sig.
Below, I'll post a couple simple example sigs, two that demonstrate how to get stats from Stanford and Berkeley's pages, and the one I'm using now. If there's enough demand, I'll add code to get stats from other pages.
If you know Perl, you'll probably already have come up with some elaborate sig. Good for you.
If you don't know Perl, however, feel free to use these examples as a starting point. While a Perl tutorial of decent length is beyond the scope of this howto, there are hundreds of Perl tutorials available via Google. Going over this one should help you figure out most of what these scripts do. If it looks intimidating, just look at a page of the tutorial and figure out how the examples use its concepts. When you understand it, go on to the next page, etc.
If you'd like a sig_gen with easy setup, excellent explanation and a good default look, make sure to go through JerMe's excellent guide here.
For something more basic, take a look at these scripts. In the first three, I've added links to the comments that explain what's happening on the next line. You're mostly on your own for the later ones.
Example 1:
output from example 1:
Example 2:
possible output from example 2:
Example 3 (default sig_gen):
possible output from example 3:
S@H stat example code
This will make a simple sig and demonstrate how to adapt it to your tastes. It's not pretty, but it's really more of a starting point than an end. A rewrite's on my todo list, but getting something that works is more important than getting something that's pretty and works.
Note that this has a lot of unnecessary code. Feel free to nuke all the code having to do with any pages you don't use stats from.
Possible output from example s@h script:
F@H stat example code
This is something of a hack. While writing it, I downloaded the page so that each test run of the script wouldn't require re-downloading it again. While using the downloaded page, I accidentally wrote it so it depended on having a downloaded page. It isn't exactly good style, but it's all my brain will allow me to do at 1:22 in the morning.
possible output from F@H example code:
This is what my current sig_gen looks like. I don't claim any great wisdom, but this is definitely not good code for a beginner to learn from. I release it into the public domain. Feel free to do with it whatever you like, although I appreciate giving credit where it's due.
Notes:
A little of the code specific to my computer. I run 3 instances of the F@H client on my SMP rig, which live in ~/f1 , ~/f2 and ~/f3 . Having similar names means that I can use 1 loop to do all the progress bar processing. The dirs where the F@H clients live is refered to by the line that reads open FH, "$home/f$n/unitinfo.txt" or die "error opening file: $!"; .
The load average is Linux-specific. To take it out, delete from open LOAD_AVG, ... to the next } .
The gradient functions look the easiest to rip off. Basically, the color_string takes a starting color, an ending color and a string and returns a string that evenly shifts from the beginning color to the ending one.
Since I wrote this, I have no idea how understandable it will be for others, and while I may have the time to help people out playing with it, be prepared to accept “no” or “later” for an answer.
In order to get this code to shop up semi-correctly, I had to put a space in all opening vbcode tags. When you see something like [ color=#333333], just delete the space.
License
All code in this post, including the linked mksig.pl script, is released into the public domain, meaning that you are free to do absolutely anything you like with it. I ask, but do not require, credit where it is due.
Enjoy!
Thanks to BeerCan for writing this section up.
Ok. Thanks to Christoph and his new perl script you can now update your signatures from a windows machine automatically.
There is one prerequisite. You need to download a win32 version of perl. You can find it here http://www.activestate.com/Products/Download/Download.plex?id=ActivePerl. I downloaded the msi version but your preference may vary. The AS Package doesn't provide any uninstall functionality, and ActiveState recommends it only if you can't use the MSI installer. Install using all of the defaults. It will create a directory c:\Perl and automatically install all necessary files there. It will also create your program groups and associate all perl extensions with perl.exe. The files we care about will end with the .pl extension. Next, we need a copy of Christoph's mksig script.
Create a directory on your c:\ drive called c:\mksig. Then, save the above linked file as c:\mksig\mksig.pl . Please make sure it has just the .pl extension and not the .pl.txt extension. If you renamed the file to mksig.pl and it still has the text icon, you need to disable hiding of known extenstions. If you do it right, the icon should turn from the notepad icon to an icon with a lizard on it. Next, execute mksig.pl. Either double click the icon or run it from a dos prompt. Both methods work. Answer all of the questions. It should create the following files in c:\mksig -- mksig.conf, sig, sig_gen.pl and sig.log. These are explaned later in this howto.
If all of those files are there, it installed correctly.
The next step is to make sure that any firewalls you're running don't block Perl.exe from connecting to the Internet. How this is accomplished will depend on your firewall, so check its documentation for more information. If you get an error like Please make sure that Perl can connect to the outside world. , you probably forgot to unblock Perl.exe.
If you want to keep your current forum sig, you should save it now, since mksig doesn't currently back up old sigs. Now when you run mksig.pl again it will update your sig Now we need to get it running as a task so it will update automatically.
Open the windows Scheduled Tasks manager. On windows XP it's start-->All Programs-->Accessories-->System Tools-->Scheduled Tasks on Win2K its Start-->Programs-->Accessories-->System Tools-->Scheduled Tasks.
Next click Add a Scheduled Task-->Next on the next screen you are going to hit Browse and browse to C:\mksig\mksig.pl and click open. Next you need to pick how often to run the task. I picked daily. Next, customize the start time to your liking and click next. On the next screen you need to enter the windows user that you would like to run the task under. This is what you use to login to windows and has nothing to do with your forum username. Make sure you enter a password or the task won't run properly. Click next. If you would like to run the task more often than daily, click on the open advanced properties checkbox and click finish. Now you can click the schedule tab and customize to your liking. It should be clear. Click ok and save and you are done.
Of course, if you want to change your sig you need to customize sig_gen.pl. That's covered in the last section of this howto.
Linux setup
Most distros today have Perl available, and installing it is usually a simple matter. For this script, you'll need Perl, LWP (which comes with the base Perl system), the Digest::MD5 module and the XML::Simple module if you're running Linux. If Digest::MD5 isn't installed on your system, just run the following command as root: perl -MCPAN -e "install Digest::MD5" or install from a distro-specific package. If you've never used CPAN before, you'll be asked a number of questions about your setup, and the overall setup can take several minutes. If you're running Gentoo, Debian or their offspring, you can install Digest-MD5 and XML-Simple or libdigest-md5-perl and libxml-simple-perl, respectively and avoid the hassle of CPAN. I don't know if similar packages are available for other distros.
Once you've got Digest::MD5 and XML::Simple installed, download mksig. To get it ready to run, do the following commands as root:
Code:
mv mksig.txt mksig
chmod 755 mksig
mv mksig /usr/bin
The previous instructions recommended changing the ownership to root:root. With versions .62 and later, having mksig owned by either root or non-root will work, although installing as root and running as non-root will prevent automatic updates form working. Since you should *never ever* run day-to-day stuff like this as root, I recommend leaving the default ownership.
Now run mksig as a normal user. Tell it your username and password when asked and use y or n to indicate if you want automatic updates. (If you enable this, mksig will check for a newer version of itself each time it runs, and if possible will download, install and run it. This feature has been tested in both windows and Linux.) Now you're ready to use the default sig. Note that mksig won't change your sig the first time it's run. This is to give you a chance to backup the old one and edit your sig generator (see next section). All user-specific information is stored in ~/.mksig and is covered in the next section.
To get mksig to run at regular intervals, you have a couple options.
The simplest is a backgrounded while loop. Pick and update interval and run (while let 1 do; mksig >/dev/null; sleep 30m; done) & and your sig will silently be updated every half-hour. If you need it to stop updating, simply run pkill sleep and it'll stop.
A similar method is to the first is while loop in the foreground. Simply run while let 1 do; mksig >/dev/null; cat ~/.mksig/sig; sleep 30m; done and your sig will be updated every 30 minutes, and the vbcode will be displayed in the terminal window.
The third method is to use a cron job. cron jobs are executed at regular intervals and are quite simple to set up. You can find Gentoo's instructions on three popular cron implementations here.
What the Files Do
There are four files: two config files and two logs. In mksig's directory, mksig.conf contains your username and password. sig_gen.pl is the Perl script (or any executable) whose output is submitted as your sig. Under Linux this file is called sig_gen because it's easier to type and Linux doesn't rely on filename extensions. sig contains the most recently submitted sig, and sig.log contains every sig mksig has submitted, along with a timestamp.
How to Get Your DC Stats in Your Sig
mksig determines what your sig will look by executing mksig.pl . Whatever it spits out will be your sig.
Below, I'll post a couple simple example sigs, two that demonstrate how to get stats from Stanford and Berkeley's pages, and the one I'm using now. If there's enough demand, I'll add code to get stats from other pages.
If you know Perl, you'll probably already have come up with some elaborate sig. Good for you.
If you don't know Perl, however, feel free to use these examples as a starting point. While a Perl tutorial of decent length is beyond the scope of this howto, there are hundreds of Perl tutorials available via Google. Going over this one should help you figure out most of what these scripts do. If it looks intimidating, just look at a page of the tutorial and figure out how the examples use its concepts. When you understand it, go on to the next page, etc.
If you'd like a sig_gen with easy setup, excellent explanation and a good default look, make sure to go through JerMe's excellent guide here.
For something more basic, take a look at these scripts. In the first three, I've added links to the comments that explain what's happening on the next line. You're mostly on your own for the later ones.
Example 1:
Code:
#!/usr/bin/perl -w
print "Hello!\n";
print "I am a sig.\n";
output from example 1:
Code:
Hello!
I am a sig.
Example 2:
Code:
#!/usr/bin/perl -w
#[url]http://www.perldoc.com/perl5.6/pod/func/localtime.html[/url]
@time = localtime;
$time[5] += 1900;
$time[4]++;
#[url]http://www.perldoc.com/perl5.6/pod/perlfunc.html#Alphabetical-Listing-of-Perl-Functions[/url]
$time_str = sprintf("%02d/%02d/%04d at %02d:%02d.\n",$time[4],$time[3],$time[5],$time[2],$time[1]);
print "Christoph's automatic sig generator v.8\n";
print "This sig was generated on $time_str";
possible output from example 2:
Code:
Christoph's automatic sig generator v.8
This sig was generated on 01/26/2005 at 23:49.
Example 3 (default sig_gen):
Code:
#!/usr/bin/perl
use LWP::UserAgent;
#Get fortune from a website because windows isn't cool enough
#to have it installed natively.
#[url]http://www.perldoc.com/perl5.6/lib/LWP/UserAgent.html[/url]
$ua= LWP::UserAgent->new();
$res=$ua->get("http://www.update.uu.se/user-cgi/bjorn/cookie.cgi?1+5");
$f_state=0;
$fortune="";
for $line (split /^/, $res->content) {
if ($line =~ m#<HR># && $f_state == 0) {
$f_state=1;
} elsif ($f_state==1) {
if ($line =~ m#</pre>#) {
$f_state=0;
} else {
$line =~ s/<[^>]*>//g;
$fortune .= $line;
}
}
}
@time = localtime;
$time[5] += 1900;
$time[4]++;
$time_str = sprintf("%02d/%02d/%04d at %02d:%02d.\n",$time[4],$time[3],$time[5],$time[2],$time[1]);
print "Christoph's automatic sig generator v.8\\n";
print "your fortune:[ font=monospace][ b]\\n";
print "$fortune";
print "\\n";
print "[/font][/b]This sig was generated on ";
print "$time_str";
possible output from example 3:
Code:
Christoph's automatic sig generator v.8
your fortune:[font=monospace][b]
You are not Morg. You are not Eymorg.
-- Kara the Eymorg, "Spock's Brain," stardate 5432.3
[/font][/b]This sig was generated on[b][font=monospace]1/27/2005 at 0:1:59[/b][/font].
S@H stat example code
This will make a simple sig and demonstrate how to adapt it to your tastes. It's not pretty, but it's really more of a starting point than an end. A rewrite's on my todo list, but getting something that works is more important than getting something that's pretty and works.
Note that this has a lot of unnecessary code. Feel free to nuke all the code having to do with any pages you don't use stats from.
Code:
#!/usr/bin/perl
use XML::Simple;
use LWP::Simple;
use LWP::UserAgent;
$boinc_id=841;
#I don't know how to find out this email for any given user.
#I assume that you have to give Berkeley your email addy when you signed up.
$berk_email = "lmester\@access.k12.wv.us";
#This is where your seti client lives. If you're in windows, make sure
#to separate directories with "\\" instead of "\".
$seti_home = "/home/ideamagnate/OCFAQ/foldsig/seti/boincdir";
#$seti_home = "c:\\something\\something";
$wu = "$seti_home/slots/0/work_unit.sah";
$state = "$seti_home/slots/0/state.sah";
$c_state = "$seti_home/client_state.xml";
#0 means disable, 1 means enable
$use_boincstats = 1;
$use_localboinc = 1;
$use_classicstats = 0;
#Local classic client monitoring isn't implemented yet.
#I'll get to it. Bug me about it if not.
$use_localclassic = 0;
#windows-specific stuff
if ($^O =~ /win/i) {
#change "/" into "\" on windows machines
$wu =~ s/\//\\\\/g;
$state =~ s/\//\\\\/g;
$c_state =~ s/\//\\\\/g;
}
#read client's state into $boinc_progress
open BOINC_STATE, "$state";
while (<BOINC_STATE>) {
if (/<prog>[[b][u][/b][/u]\d\.]*<\/prog>/) {
s/<prog>([[b][u][/b][/u]\d\.]*)<\/prog>/$1/;
chomp;
$boinc_progress = $_;
}
}
close BOINC_STATE;
#get current wu's name from appropriate place
open BOINC_WU, "$wu";
while (<BOINC_WU>) {
s/<soft_link>\.\.\\\.\.\\projects\\setiathome\.berkeley\.edu\\//;
s/<\/soft_link>//;
chomp;
$wu_name = $_;
}
close BOINC_WU;
#parse and extract info from xml file
$cs_xml = XMLin($c_state);
$total_credit = $cs_xml->{project}->{host_total_credit};
###########################
#get BOINC stats from web##
###########################
if ($use_boincstats) {
#[url]http://www.boincstats.com/stats/user_graph.php?pr=sah&id=841[/url]
$boinc_page = "http://www.boincstats.com/stats/user_graph.php?pr=sah&id=$boinc_id";
$local_boinc_cache = "boinc.html";
getstore($boinc_page, $local_boinc_cache);
if (! -e $local_boinc_cache) {
print "get the file, stupid\n";
exit;
}
#extract stats from boinc page
open BC, "$local_boinc_cache";
while(<BC>){
if (/Detailed statistics for<br>/) {
$_ =~ s/"([[b][u][/b][/u]a-zA-Z][[b][u][/b][/u]\w\s]*)/$1/;
$boinc_user = $1;
} elsif (/>User ID</) {
<BC>;
$boinc_id = <BC>;
chomp($boinc_id);
$boinc_id =~ s/\s*(\d*).*/$1/;
} elsif (/<td>Total Credit</) {
<BC>;
$boinc_total_credit = <BC>;
chomp($boinc_total_credit);
$boinc_total_credit =~ s/\s*([[b][u][/b][/u]\d\.\,]*)<.*/$1/;
} elsif (/Number of hosts/) {
<BC>;
$boinc_hostcount = <BC>;
chomp($boinc_hostcount);
$boinc_hostcount =~ s/\s*([[b][u][/b][/u]\d,]*).*/$1/;
} elsif (/World Position/) {
<BC>;
$boinc_overall_rank = <BC>;
chomp($boinc_overall_rank);
$boinc_overall_rank =~ s/\s*([[b][u][/b][/u]\d,]*).*/$1/;
} elsif (/than % of all users/) {
$boinc_percentile = <BC>;
chomp($boinc_percentile);
$boinc_percentile =~ s/<td>(\d*.\d*)%.*/$1/;
} elsif (/Position in Team/) {
<BC>;
$boinc_team_rank = <BC>;
chomp($boinc_team_rank);
$boinc_team_rank =~ s/\s*([[b][u][/b][/u]\d,]*).*/$1/;
}
}
close BC;
}
##########################
#get stats from Berkeley##
##########################
if ($use_classicstats) {
$berk_url = "http://setiathome2.ssl.berkeley.edu/fcgi-bin/fcgi";
$berk_url = "http://setiathome2.ssl.berkeley.edu/fcgi-bin/fcgi?email=";
$berk_url .= "$berk_email&cmd=user_xml";
$berk_url =~ s/@/%40/;
#[url]http://setiathome2.ssl.berkeley.edu/fcgi-bin/fcgi?email=lmester%40access.k12.wv.us+&cmd=user_xml[/url]
$ua = LWP::UserAgent->new();
$berk_xml = $ua->get( "$berk_url");
#$berk_xml = $ua->get( "$berk_url",
# [[b][u][/b][/u] "cmd" => "user_xml",
# "email" => $berk_email]);
#extract info from Berkeley's not-quite XML
foreach $line (split /^/, $berk_xml->content) {
if ($line =~ /<name>/) {
for ($cl_name = $line) {
chomp;
s/<[[b][u][/b][/u]^>]*>//g;
s/\s*//;
}
} elsif ($line =~ /numresults/) {
for ($cl_num_results = $line) {
chomp;
s/\s*<[[b][u][/b][/u]a-z_]*>([[b][u][/b][/u]^<]*).*/$1/;
}
} elsif ($line =~ /cputime/) {
for ($cl_user_time = $line) {
chomp;
s/\s*<[[b][u][/b][/u]a-z_]*>([[b][u][/b][/u]^<]*).*/$1/;
}
} elsif ($line =~ /resultsperday/) {
for ($cl_daily_results = $line) {
chomp;
s/\s*<[[b][u][/b][/u]a-z_]*>([[b][u][/b][/u]^<]*).*/$1/;
}
} elsif ($line =~ /<rank>/) {
for ($cl_rank = $line) {
chomp;
s/\s*<[[b][u][/b][/u]a-z_]*>([[b][u][/b][/u]^<]*).*/$1/;
}
}
}
}
#These are boring examples of how to print out stuff.
#Hopefully, you'll come up with something more interesting. ;)
#print "LOCAL BOINC INFO:\n";
#print "progress is $boinc_progress\n";
#print "total credits is $total_credit\n";
#print "wu name is $wu_name\n";
#if ($use_boincstats) {
# print "BOINC INFO:\n";
# print "username = $boinc_user\n";
# print "ID = $boinc_id\n";
# print "total credit = $boinc_total_credit\n";
# print "hosts = $boinc_hostcount\n";
# print "overall rank = $boinc_overall_rank\n";
# print "percentile = $boinc_percentile\n";
# print "team rank = $boinc_team_rank\n";
#}
#if ($use_classicstats) {
# print "CLASSIC INFO:\n";
# print "username is $cl_name\n";
# print "number of results is $cl_num_results\n";
# print "total cpu time donated is $cl_user_time\n";
# print "results per day is $cl_daily_results\n";
# print "overall rank is $cl_rank\n";
#}
#make a pretty progress bar
#length of progress bar
$prog_len = 25;
#starting and ending colors for progress bar
$prog_start = "ff0000";
$prog_end = "0000ff";
#colors for the percent indicator
$pct_begin = "ff0000";
$pct_end = "00ff00";
#You can't see me.
$invisible = "333333";
$filled_bars = sprintf("%.0f",($boinc_progress)*$prog_len);
$pct_prog = sprintf("%.0f",$boinc_progress*100);
$empty_bars = $prog_len - $filled_bars;
#print "$pct_prog% progress, $filled_bars chars, $empty_bars unfilled\n";
$prog_bar = "{";
for($i=0; $i < $filled_bars; $i++) {
$progress_char = "~";
$color=color_grad($[b][u][/b][/u]prog_start,$prog_end,$prog_len,$i);
$prog_bar .= "\[[b][u][/b][/u]color=#$color\]$progress_char\[[b][u][/b][/u]/color\]";
}
$prog_bar .= "\[[b][u][/b][/u]color=#$invisible\]";
$prog_bar .= "_" x $empty_bars;
$prog_bar .= "\[[b][u][/b][/u]/color\]";
$prog_bar .= "}";
#now $prog_bar looks like "{****___}" but with color tags
$pct_len = length $pct_prog;
$spc_len = 4 - $pct_len;
$spc = "_" x $spc_len;
$prog_bar =~ s/$/\[[b][u][/b][/u]color=#$invisible\]$spc\[[b][u][/b][/u]\/color\]/;
#print "$prog_bar\n";
$pct_color = color_grad($pct_begin,$pct_end,100,$pct_prog);
$prog_bar =~ s/$/\[[b][u][/b][/u]color=#$pct_color\]$pct_prog\%\[[b][u][/b][/u]\/color\]/;
#progress bar is done!
#It should look like "{~~~~~~_____} 34%" but will COLOR.
$crunch_start = "ff00ff";
$crunch_end = "00ff00";
$crunch_str = color_string($crunch_start,$crunch_end,"Crunching for Overclockers.com!");
#99% means you're in the top 1%, so put that in $top_pct
$top_pct = sprintf("%.2f",100.0 - $boinc_percentile);
$rank = $boinc_overall_rank;
$boinc_total_credit =~ s/,//g;
$credits = sprintf("%.0f",$boinc_total_credit);
##################################
#This is where stuff is printed.##
##################################
print "[[b][u][/b][/u]b]$crunch_str\[[b][u][/b][/u]\/b]\n";
print "ranked #$rank in the top $top_pct% with $credits credits\n";
print "current wu: $prog_bar completed\n";
#Given a starting color, an ending color, a total number of steps, the
#current step and an optional error, return the color that corresponds
#to that step in a gradient.
sub color_grad
{
$start_rgb = shift; #"ff00ff"
$end_rgb = shift; #"00ff00"
$last_step = shift; #"36"
$curr_step = shift; #"12"
#error is random variation, in decimal shades
$error = shift;
$periods = shift;
$fun = shift;
return color_grad_fun($start_rgb,$end_rgb,$curr_step/$last_step,$error,$periods,$fun);
}
sub color_grad_fun
{
my $start_rgb = shift; #"ff00ff"
my $end_rgb = shift; #"00ff00"
my $curr_step = shift; #floating pt number, modded between 0 and 1. inclusive
#OPTIONAL ARG: error is random variation, in decimal shades
my $error = shift;
$error = 0 if (! defined $error);
#OPTIONAL ARG: how many times to cycle through the above function
my $periods = shift;
$periods = 1 if (! defined $periods);
#OPTIONAL ARG: function with a period of 1 and a range 0-1 to determine color distribution
my $fun = shift;
#This function has the same behavior as the previous implementation.
$fun = sub {my $x = shift; return ((255*$x % 255)/255);} if (!defined $fun);
$error /= 255;
for $curr (0,1,2) {
$min = hex(substr($start_rgb,$curr*2,2)) / 255;
$max = hex(substr($end_rgb,$curr*2,2)) / 255;
$range = $max - $min;
$funval = &$fun($curr_step*$periods);
#print "max is $max, min is $min, range is $range, step is $curr_step,";
$color[[b][u][/b][/u]$curr] = ($min + $range*$funval);
if ($error == 0) {
$my_error = 0;
} else {
$my_error = (rand 2*$error) - $error;
}
while (($color[[b][u][/b][/u]$curr]+$my_error) < 0 || ($color[[b][u][/b][/u]$curr]+$my_error) > 1) {
$my_error = ((rand 2*$error) - $error);
}
#print "using error of $my_error, max is $error\n";
#$norml = $color[[b][u][/b][/u]$curr]*255;
#print "normal color is $norml, ";
$color[[b][u][/b][/u]$curr] += $my_error;
$color[[b][u][/b][/u]$curr] *= 255;
#$color[[b][u][/b][/u]$curr] = sprintf("%.0f",)
#print "color is $color[[b][u][/b][/u]$curr]\n";
}
return sprintf("%02x%02x%02x",$color[[b][u][/b][/u]0],$color[[b][u][/b][/u]1],$color[[b][u][/b][/u]2]);
}
#Make a string shift from start_rgb to end_rgb, with an optional random error
sub color_string
{
$start_rgb = shift;
$end_rgb = shift;
$string = shift;
#optional args below
$error = shift;
$error = 0 if (! defined $error);
$periods = shift;
$periods = 1 if (! defined $periods);
$fun = shift;
$fun = sub {my $x = shift; return ((255*$x % 256)/255);} if (!defined $fun);
#ugly bug if this isn't declared my
my $color_str;
my $str_len = length $string;
my $prev_color = "gggggg";
my $prev_char = "?";
$i=0;
foreach $char (split //, $string) {
$fstr_color=color_grad($start_rgb,$end_rgb,$s[b][u][/b][/u]tr_len,$i++,$error,$periods,$fun);
#vb3 seems to treat individually colored spaces an non-printable
if ($char eq " ") {
#if this is the same char we just printed, we can use less bbcode
if ($prev_char eq " " && $color_str =~ /\[[b][u][/b][/u]\/color\]$/) {
$color_str =~ s/.$/\.\.\[[b][u][/b][/u]\/color\]/;
#we only need an invisible char if there's more than one in a row
} elsif ($prev_char eq " ") {
$color_str =~ s/.$/\[[b][u][/b][/u]color=\#$invisible\]\.\.\[[b][u][/b][/u]\/color\]/;
#else we've got a single space
} else {
#$color_str .= "\[[b][u][/b][/u]color\=\#$invisible\].\[[b][u][/b][/u]/color\]";
$color_str .= " ";
}
$fstr_color = $invisible;
} else {
#if this is the same color we just used, we can reuse tag
if ($prev_color eq $fstr_color) {
$color_str =~ s/(.)\[[b][u][/b][/u]\/color\]$/$1$char\[[b][u][/b][/u]\/color\]/;
} else {
$color_str .= "\[[b][u][/b][/u]color\=\#$fstr_color\]$char\[[b][u][/b][/u]/color\]";
}
}
$prev_char = $char;
$prev_color = $fstr_color;
}
#print "COLOR STRING: returning $color_str\n";
return $color_str;
}
Possible output from example s@h script:
Code:
[b][color=#ff00ff]C[/color][color=#f708f7]r[/color][color=#ef10ef]u[/color][color=#e718e7]n[/color][color=#df20df]c[/color][color=#d629d6]h[/color][color=#ce31ce]i[/color][color=#c639c6]n[/color][color=#be41be]g[/color] [color=#ad52ad]f[/color][color=#a45aa4]o[/color][color=#9d629d]r[/color] [color=#8c738c]O[/color][color=#837b83]v[/color][color=#7c837c]e[/color][color=#748b74]r[/color][color=#6a946a]c[/color][color=#629c62]l[/color][color=#5aa45a]o[/color][color=#52ac52]c[/color][color=#4ab44a]k[/color][color=#41bd41]e[/color][color=#39c539]r[/color][color=#31cd31]s[/color][color=#29d529].[/color][color=#20de20]c[/color][color=#18e618]o[/color][color=#10ee10]m[/color][color=#08f608]![/color][/b]
ranked #1 in the top 0.11% with 495535 credits
current wu: {[color=#ff0000]~[/color][color=#f5000a]~[/color][color=#eb0014]~[/color][color=#e1001e]~[/color][color=#d70028]~[/color][color=#cc0033]~[/color][color=#c2003d]~[/color][color=#b80047]~[/color][color=#ae0051]~[/color][color=#a3005b]~[/color][color=#990066]~[/color][color=#8f0070]~[/color][color=#84007a]~[/color][color=#7a0084]~[/color][color=#333333]___________[/color]}[color=#333333]__[/color][color=#718e00]56%[/color] completed
F@H stat example code
This is something of a hack. While writing it, I downloaded the page so that each test run of the script wouldn't require re-downloading it again. While using the downloaded page, I accidentally wrote it so it depended on having a downloaded page. It isn't exactly good style, but it's all my brain will allow me to do at 1:22 in the morning.
Code:
#!/usr/bin/perl -w
use LWP::UserAgent;
#Stanford's stats
$stfd_username="PMSFishy";
$team_num=32;
$stfd_url="http://vspx27.stanford.edu/cgi-bin/main.py?qtype=userpage&username=$stfd_username&teamnum=$team_num";
$ua = LWP::UserAgent->new();
$page = $ua->get($stfd_url);
open STFD_PAGE,">stfd.html";
print STFD_PAGE $page->content;
close STFD_PAGE;
open STFD_PAGE, "stfd.html";
while (<STFD_PAGE>){
chop;
if (/updated/) {
s/.*updated://;
s/ //;
$stfd_last_update=$_;
}
if (/last work unit/) {
$_ = <STFD_PAGE>;
s/<[^>]*>//g;
s/[^\d]*//;
s/(\d\d:\d\d:\d\d)[^\d]*/$1/;
$stfd_last_wu=$_;
}
if (/Total score/) {
$_ = <STFD_PAGE>;
s/<[^>]*>//g;
s/[^\d]*//g;
$stfd_total_score=$_;
}
if (/Overall rank/) {
$_ = <STFD_PAGE>;
s/.*\d> //;
s/([\d]* of [\d]*)[^\d]*/$1/;
$stfd_overall_rank=$_;
}
if (/50 days/) {
$_ = <STFD_PAGE>;
s/.*\d> //;
/([\d]*)/;
$stfd_cpus_50days=$1;
}
if (/7 days/) {
$_ = <STFD_PAGE>;
s/.*\d> //;
/([\d]*)/;
$stfd_cpus_7days=$1;
}
if (/> WU/) {
$_ = <STFD_PAGE>;
$_ = <STFD_PAGE>;
s/[^\d]*//;
s/([\d]*)//;
$stfd_wu_total=$1;
}
}
print "date of last WU: $stfd_last_wu\n";
print "total score: $stfd_total_score\n";
print "overall rank: $stfd_overall_rank\n";
print "active cpus (50d): $stfd_cpus_50days\n";
print "active cpus (7d): $stfd_cpus_7days\n";
print "WU total: $stfd_wu_total\n";
print "stats last updated: $stfd_last_update\n";
possible output from F@H example code:
Code:
date of last WU: 2005-01-27 16:16:34
total score: 698940
overall rank: 166 of 419739
active cpus (50d): 28
active cpus (7d): 10
WU total: 16330
stats last updated: Fri Jan 28 00:29:16 PST 2005
This is what my current sig_gen looks like. I don't claim any great wisdom, but this is definitely not good code for a beginner to learn from. I release it into the public domain. Feel free to do with it whatever you like, although I appreciate giving credit where it's due.
Notes:
A little of the code specific to my computer. I run 3 instances of the F@H client on my SMP rig, which live in ~/f1 , ~/f2 and ~/f3 . Having similar names means that I can use 1 loop to do all the progress bar processing. The dirs where the F@H clients live is refered to by the line that reads open FH, "$home/f$n/unitinfo.txt" or die "error opening file: $!"; .
The load average is Linux-specific. To take it out, delete from open LOAD_AVG, ... to the next } .
The gradient functions look the easiest to rip off. Basically, the color_string takes a starting color, an ending color and a string and returns a string that evenly shifts from the beginning color to the ending one.
Since I wrote this, I have no idea how understandable it will be for others, and while I may have the time to help people out playing with it, be prepared to accept “no” or “later” for an answer.
In order to get this code to shop up semi-correctly, I had to put a space in all opening vbcode tags. When you see something like [ color=#333333], just delete the space.
Code:
#!/usr/bin/perl -w
use LWP::UserAgent;
#might be cool:
# * have the color of the "45%" change as they approach 100
# * Use. More. Colors.
# * use a funky character helix for the progress bar
# * find random quotes from the forums, maybe threads you've posted in
# * make everything invisible except for colored letters spelling out "FOLD!"
# * get gradient randomization working properly
#string to use for progress indicator
$progress_char="o";
#how many of the above char to use at 100%
$prog_len = 10;
#starting/ending color for progress indicator
$prog_start = "333333";
$prog_end = "ffd700";
#$prog_start = "ff00ff";
#$prog_end = "ffff00";
$fstr_start = "ff00ff";
$fstr_end = "ffff00";
$ldavg_begin = "00ff00";
$ldavg_end = "ff0000";
$pct_begin = "8f9aff";
$pct_end = "ff0000";
#how many times to cycle through the progress indicator gradient
$prog_cycles = 1;
#a place to hang your hat
$home = glob "~/";
#you can't see me
$invisible="333333";
@time = localtime;
$time[[b][u][/b][/u]5] += 1900;
$time[[b][u][/b][/u]4]++;
#get local info about current WUs
$stfd_cache = "stfd.html";
#$invisible_char = "\[[b][u][/b][/u]color\=\#$invisible\].\[[b][u][/b][/u]/color\]";
$min_pct = 100;
for $n (1, 2, 3) {
$f_name = "";
$f_prog = "";
open FH, "$home/f$n/unitinfo.txt" or die "error opening file: $!";
while(<FH>){
chomp;
$f_name .= $_;
}
close FH;
$f_prog = $f_name;
$f_name =~ s/.*Name://;
$f_name =~ s/Download.*//;
$f_name = color_string("ffff00","ff0000",$f_name,50);
for ($f_prog) {
s/.*Progress: //;
s/\%.*//;
$pct_prog = $_;
#$pct_prog = 100;
#this is a *recommended* way of rounding
$filled_bars = sprintf("%.0f",($pct_prog/100)*$prog_len);
$empty_bars = $prog_len - $filled_bars;
#print "$pct_prog% progress, $filled_bars chars, $empty_bars unfilled\n";
$f_prog = "{";
for($i=0; $i < $filled_bars; $i++) {
$color=color_grad($prog_start,$prog_end,$prog_len/$prog_cycles,$i);
$f_prog .= "\[[b][u][/b][/u]color=#$color\]$progress_char\[[b][u][/b][/u]/color\]";
}
$f_prog .= "[[b][u][/b][/u]color=#$invisible]";
$f_prog .= "_" x $empty_bars;
$f_prog .= "[[b][u][/b][/u]/color]";
$f_prog .= "}";
# now looks like "{****___}" but with color tags
$f_prog .= "$pct_prog%";
if ($pct_prog > 99) {
s/(\d\d\d%)/[[b][u][/b][/u]color=#$invisible]_[[b][u][/b][/u]\/color]$1/;
} elsif ($pct_prog >9) {
s/(\d\d%)/[[b][u][/b][/u]color=#$invisible]__[[b][u][/b][/u]\/color]$1/;
} else {
s/(\d%)/[[b][u][/b][/u]color=#$invisible]___[[b][u][/b][/u]\/color]$1/;
}
#print "$f_prog\n";
$pct_color = color_grad($pct_begin,$pct_end,100,$pct_prog);
s/(\d*%)/\[[b][u][/b][/u]color=#$pct_color\]$1\[[b][u][/b][/u]\/color\]/;
$min_pct = $pct_prog if ($pct_prog < $min_pct);
}
$f_name[[b][u][/b][/u]$n-1] = $f_name;
$f_prog[[b][u][/b][/u]$n-1] = $f_prog;
}
#get stats from Intarw3b
$user="IdeaMagnate";
$team=32;
#only download stanford's page every hour ( = 60*60 seconds)
#(stat(file))[[b][u][/b][/u]9] is the time since file was last modified, in seconds
if ((! -e $stfd_cache) || (time+(60*60) > (stat($stfd_cache))[[b][u][/b][/u]9])) {
$ua = LWP::UserAgent->new();
$stfd_page = $ua->get("http://vspx27.stanford.edu/cgi-bin/main.py?qtype=userpage&teamnum=$team&username=$user");
if (! $stfd_page->is_success) {
die "Couldn't download Stanford's page.";
}
open STFD_CACHE, ">$stfd_cache";
print STFD_CACHE $stfd_page->content;
close STFD_CACHE;
$s_content = $stfd_page->content;
} else {
open STFD_CACHE, "$stfd_cache";
while(<STFD_CACHE>) {
$s_content .= $_;
}
close STFD_CACHE;
}
$pts = $s_content;
$wus = $s_content;
$cpu = $s_content;
for ($pts) {
s/[[b][u][/b][/u]\n\r]//g;
s/.*Total score//g;
s/[[b][u][/b][/u]^\d]*//;
s/4> (\d*).*/$1/;
}
for ($wus) {
s/[[b][u][/b][/u]\n\r]//g;
s/.*WU</</g;
s/<[[b][u][/b][/u]^>]*>//g;
s/\w*(\d\d\d\d).*/$1/;
s/ //g;
s/\t//g;
}
for ($cpu) {
s/[[b][u][/b][/u]\n\r]//g;
s/.*Active processors//;
s/<[[b][u][/b][/u]^>]*>//g;
s/[[b][u][/b][/u] \t]//g;
s/\([[b][u][/b][/u]^\)]*\)//g;
s/(\d*).*/$1/g;
}
open LOAD_AVG, "uptime |";
$load_avg = <LOAD_AVG>;
close LOAD_AVG;
for ($load_avg) {
s/.*load average/\[[b][u][/b][/u]u\]Load Avg\[[b][u][/b][/u]\/u\]/;
s/,//g;
/(\d\d*\.\d\d*) *(\d\d*\.\d\d*) *(\d\d*\.\d\d*)/;
$c1 = color_grad($ldavg_begin,$ldavg_end,450,$1*100);
$c2 = color_grad($ldavg_begin,$ldavg_end,450,$2*100);
$c3 = color_grad($ldavg_begin,$ldavg_end,450,$3*100);
s/(\d\d*\.\d\d*) *(\d\d*\.\d\d*) *(\d\d*\.\d\d*)/\[[b][u][/b][/u]color=#$c1\]$1\[[b][u][/b][/u]\/color] [[b][u][/b][/u]color=#$c2\]$2\[[b][u][/b][/u]\/color] [[b][u][/b][/u]color=#$c3\]$3\[[b][u][/b][/u]\/color]/;
chomp;
}
$pts_len=7;
$wus_len=9;
$cpu_len=5;
$spcr1= "[[b][u][/b][/u]color=#$invisible]" . "_" x 4 . "[[b][u][/b][/u]/color]";
$spcr2= "[[b][u][/b][/u]color=#$invisible]" . "_" x 4 . "[[b][u][/b][/u]/color]";
$spcr3= "[[b][u][/b][/u]color=#$invisible]" . "_" x 8 . "[[b][u][/b][/u]/color]";
$pts_pad = $pts_len - (length $pts);
$wus_pad = $wus_len - (length $wus);
$cpu_pad = $cpu_len - (length $cpu);
$pts = "[[b][u][/b][/u]color=#$invisible]" . "_" x $pts_pad . "[[b][u][/b][/u]/color]" . $pts . $spcr1;
$wus = "[[b][u][/b][/u]color=#$invisible]" . "_" x $wus_pad . "[[b][u][/b][/u]/color]" . $wus . $spcr1;
$cpu = "[[b][u][/b][/u]color=#$invisible]" . "_" x $cpu_pad . "[[b][u][/b][/u]/color]" . $cpu . $spcr1;
$folding_str = color_string($fstr_start,$fstr_end,"Folding for team 32!");
$time_str = sprintf("%02d/%02d/%04d at %02d:%02d.\n",$time[[b][u][/b][/u]4],$time[[b][u][/b][/u]3],$time[[b][u][/b][/u]5],$time[[b][u][/b][/u]2],$time[[b][u][/b][/u]1]);
print "[[b][u][/b][/u]font=monospace][[b][u][/b][/u]size=1][[b][u][/b][/u]b][[b][u][/b][/u]url=http://www.ocforums.com/forumdisplay.php?f=25]Alt OS Lead Senior[[b][u][/b][/u]/url] | [[b][u][/b][/u]url=http://del.icio.us/ideamagnate]del.icio.us[[b][u][/b][/u]/url] | [[b][u][/b][/u]url=http://heatware.com/eval.php?id=1958]Heatware[[b][u][/b][/u]/url] | [[b][u][/b][/u]url=http://www.ocforums.com/showthread.php?t=328963]FAQ 2.0[[b][u][/b][/u]/url] | [[b][u][/b][/u]url=http://gladstone.uoregon.edu/~cotto1/ochosting/jellyfish.txt]Jellyfish[[b][u][/b][/u]/url][[b][u][/b][/u]/font][[b][u][/b][/u]/b]\n";
print "[[b][u][/b][/u]b][[b][u][/b][/u]color=#CCCC00]Did someone go out of his way to help you? Show your appreciation [[b][u][/b][/u]url=http://www.ocforums.com/showthread.php?t=345204]here[[b][u][/b][/u]/url].[[b][u][/b][/u]/color][[b][u][/b][/u]/b][[b][u][/b][/u]/size]\n\n";
print "[[b][u][/b][/u]font=monospace][[b][u][/b][/u]b]$folding_str\n";
print "[[b][u][/b][/u]u]My stats[[b][u][/b][/u]/u]:".$spcr3. "[[b][u][/b][/u]u]My current WUs[[b][u][/b][/u]/u]:$spcr2$load_avg\n";
print "score:$pts". "$f_prog[[b][u][/b][/u]0] :$f_name[[b][u][/b][/u]0]\n";
print "WUs:$wus". "$f_prog[[b][u][/b][/u]1] :$f_name[[b][u][/b][/u]1]\n";
print "clients:$cpu". "$f_prog[[b][u][/b][/u]2] :$f_name[[b][u][/b][/u]2]\n";
# stat: 03030 {****** } 61% : p1139_p1133_L939_K12M_355K
print "[[b][u][/b][/u]/font][[b][u][/b][/u]/b]This sig was generated on $time_str";
sub color_grad
{
$start_rgb = shift; #"ff00ff"
$end_rgb = shift; #"00ff00"
$last_step = shift; #"36"
$curr_step = shift; #"12"
#error is random variation, in decimal shades
$error = shift;
$error = 0 if (! defined $error);
$curr_step %= $last_step;
for $curr (0,1,2) {
$error[[b][u][/b][/u]$curr] = (rand 2*$error) - $error;
$start[[b][u][/b][/u]$curr] = hex(substr($start_rgb,$curr*2,2));
$end[[b][u][/b][/u]$curr] = hex(substr($end_rgb,$curr*2,2));
$step[[b][u][/b][/u]$curr] = ($end[[b][u][/b][/u]$curr] - $start[[b][u][/b][/u]$curr]) / $last_step;
$color[[b][u][/b][/u]$curr] = $start[[b][u][/b][/u]$curr] + $step[[b][u][/b][/u]$curr]*$curr_step + $error[[b][u][/b][/u]$curr];
#ensure that the color is within the possible range
while ((0 > $color[[b][u][/b][/u]$curr]) || ($color[[b][u][/b][/u]$curr] > 255)) {
$error[[b][u][/b][/u]$curr] = (rand 2)*$error - $error;
$color[[b][u][/b][/u]$curr] = $start[[b][u][/b][/u]$curr]+ $step[[b][u][/b][/u]$curr]*$curr_step + $error[[b][u][/b][/u]$curr];
}
#print "start is $start[[b][u][/b][/u]$curr], end is $end[[b][u][/b][/u]$curr], step is $step[[b][u][/b][/u]$curr], color is $color[[b][u][/b][/u]$curr]\n";
}
return sprintf("%02x%02x%02x",$color[[b][u][/b][/u]0],$color[[b][u][/b][/u]1],$color[[b][u][/b][/u]2]);
}
sub color_string
{
$start_rgb = shift;
$end_rgb = shift;
$string = shift;
$error = shift;
#print "COLOR STRING: passed $string\n";
$error = 0 if (! defined $error);
#ugly bug if this isn't declared my
my $color_string;
$str_len = length $string;
$i=0;
foreach $char (split //, $string) {
$fstr_color=color_grad($start_rgb,$end_rgb,$st[b][/b]r_len,$i++,$error);
#vb3 seems to treat individually colored spaces an non-printable
if ($char eq " ") {
$color_string .= "\[[b][u][/b][/u]color\=\#$invisible\].\[[b][u][/b][/u]/color\]";
} else {
$color_string .= "\[[b][u][/b][/u]color\=\#$fstr_color\]$char\[[b][u][/b][/u]/color\]";
}
}
#print "COLOR STRING: returning $color_string\n";
return $color_string;
}
License
All code in this post, including the linked mksig.pl script, is released into the public domain, meaning that you are free to do absolutely anything you like with it. I ask, but do not require, credit where it is due.
Enjoy!
Last edited: