Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Controlling Xchat in #cbstream

by Tanktalus (Canon)
on Aug 12, 2008 at 15:34 UTC ( [id://703918]=CUFP: print w/replies, xml ) Need Help??

Update 8: Moved code to github. All future updates will be there.

With the new cbstream interface for logging in, I was prompted, in my Laziness, to write a quick-login for Xchat. Once I learned how to do that, other, easier things came up just 'cuz they were neat (and prompted some discussion). And then Intrepid asked for a script that could get the IRC channel to look right. I agreed, and since I seemed to have half of it done (the learning-curve part), I thought I'd try. It didn't take long.

Right now, #cbstream looks like "cbstream" is writing everything, even though each line starts with the real speaker in brackets. The goal was to drop 'cbstream' from the speaker position, and replace it with the PM nick that really initiated the line. This turned out to be not too bad - a few regexes, a few pointers from some gurus in #xchat, and problem was solved. Well, almost. The '/me' action still looked funny. Eventually, with a bit more help from the gurus in #xchat, and escaping a magic character in a regex, all started working. So, for anyone using Xchat to communicate with PM's CB via FreeNode's #cbstream channel, here is my complete script. I call it "cbstream.pl" and put it in my xchat directory (for me, "~/.xchat2", YMMV, especially on Windows):

#! /usr/bin/perl use strict; use warnings; my $name = 'cbstream'; my $version = 0.08; # what I need... use YAML::Syck; use Data::Diver qw(DiveVal Dive DiveRef); use LWP::Simple qw(); use XML::Twig; # debugging tools... use IO::Handle; open my $log, '>', File::Spec->catfile( Xchat::get_info('xchatdir'), "$name.log" ) or die "Can't open $name.log: $!"; $log->autoflush(); sub LOG { print $log scalar localtime, ": ", @_, "\n" } LOG 'starting up...'; use Data::Dumper; =head1 NAME CBStream Xchat plugin - Handle all the weirdnesses of the IRC-to-Chatt +erBox bridge that is FreeNode's #cbstream =head1 INSTALLATION First, install the required modules. (See the top of the file for the + use statements.) Next, copy this file to your Xchat directory. For me, that is ~/.xcha +t2. YMMV, especially on Windows. The file should be named 'cbstream.pl', +though only the extention is important. Then create a configuration file in the same directory called "cbstrea +m.yaml". It should look like: --- cbstream: pmpassword: myreallylongpasswordthatIshouldneverforget pmuser: Tanktalus That's the minimal configuration required. Now load the plugin (I thi +nk this happens automatically on Xchat's start-up, but you can do it manu +ally with the /load command). You're ready to go. =head1 CONFIGURATION OPTIONS Other than pmpassword and pmuser, other options include: =over 4 =item nick-prefix =item nick-suffix Here you can customise how the nicks will appear in Xchat. I set thes +e this way: cbstream: nick-prefix: "[" nick-suffix: "]" Then nicks will show up like "[tye]" instead of just "tye". This make +s it easier for cut&paste for me. =item ignore_mode If you have anyone being ignored, you can actually be told when you re +ceive a message from them. You can set this mode to C<brief> to get a notic +e that a message has been ignored (and from whom), or C<verbose> to not +only get the notice, but the entire message they sent. =back =head1 WHAT IT DOES By default, it replaces the nick 'cbstream' on the left of the line in + Xchat with the real nick (see nick-prefix and nick-suffix above). It also e +nables some new commands: =head1 COMMANDS =over 4 =item cblog This will do a quick login with the cbstream server, joining the #cbst +ream-login channel, posting your plogin command, and then closing the tabs. You +may not end up back in the channel you started from. You only need to do this whenever cbstream is restarted, not just any +time you join IRC. You still need to be registered with FreeNode's NickSer +v. =item cbig This will reload the /ignore's from PerlMonks to be used in IRC. Tran +sfering back and forth is NOT automatic, and would cause undue stress on the P +M server, slowing down your IRC experience. When you run this command, your system will retrieve your ignore list +from Perlmonks, and then resolve each user number to a name. This lookup w +ill use a local cache to avoid hitting the server too much for each number + to name lookup. This also means that if you unignore on Perlmonks, the user m +ay still show up in the cbstream->ignore->byid fields in your conf file. + This is normal and won't interfere with anything. =item cbignore This will add an ignore, and save it to your PM user for use with othe +r CB applications (such as Full Page Chat). =item cbunignore This will remove an ignore, and remove it from your PM user as well. =item cbmsg This will send a /msg to whomever you specify. Any private response w +ill not be seen in IRC. =item cbset This will set a configuration value. Note that there's no validation +here: you can set anything you want. /cbset foo bar This will persist via the config file for future use. =item cbget This will retrieve a configuration value from the config file. =item cbrm This will remove a configuration key from the config file. =back =head1 TODO * validation for cbset - ensuring you're setting real keys to allowed +values =cut { use File::Spec; my $conf; my $conf_file = File::Spec->catfile(Xchat::get_info('xchatdir'), $ +name . '.yaml'); sub _conf { $conf ||= LoadFile($conf_file); $conf } sub get_conf { my @var = @_; Dive(_conf, @var); } sub set_conf { my $val = pop; DiveVal(_conf, @_) = $val; } sub rm_conf { my $val = pop; my $rm = Dive(_conf, @_); if ($rm) { delete $rm->{$val}; } } sub save_conf { DumpFile($conf_file, $conf) if $conf; } } Xchat::register($name, $version); Xchat::hook_command('cblog', sub { Xchat::command 'join #cbstream-logi +n'; Xchat::EAT_ALL }); Xchat::hook_print('You Join', \&JoinCBStreamLogin); Xchat::hook_server('PRIVMSG', \&LeaveCBStreamLogin); # once logged in, tell cbstream what our id and pw is. sub JoinCBStreamLogin { my $info = shift; if (Xchat::get_info('network') eq 'FreeNode') { if ($info->[1] eq '#cbstream-login') { my $id = get_conf('cbstream','pmuser'); my $pw = get_conf('cbstream','pmpassword'); if ($id and $pw) { Xchat::set_context('#cbstream-login'); Xchat::command("say plogin $id $pw"); } return Xchat::EAT_NONE; } } Xchat::EAT_NONE; } sub LeaveCBStreamLogin { my $msg = shift; my $nth = shift; if (Xchat::get_info('network') eq 'FreeNode') { if ($msg->[0] =~ /^:cbstream!/ and $msg->[1] eq 'PRIVMSG' and lc $msg->[2] eq lc Xchat::get_info('nick') ) { if ($nth->[3] =~ /You are now persistently logged in as pe +rlmonks user/) { Xchat::set_context('cbstream'); Xchat::command('close'); Xchat::set_context('#cbstream-login'); Xchat::command('close'); Xchat::set_context('#cbstream'); (my $realmsg = $nth->[3]) =~ s/^:\+//; Xchat::print("CBLOGIN: $realmsg"); return Xchat::EAT_ALL; } else { Xchat::set_context('#cbstream'); (my $realmsg = $nth->[3]) =~ s/^:\+//; Xchat::print("CB: $realmsg"); return Xchat::EAT_ALL; } } } return Xchat::EAT_NONE; } # check for ignored users... Xchat::hook_command('cbig', \&get_ignored); sub get_ignored { Xchat::print("Gathering ignores from Perlmonks"); rm_conf(qw(cbstream ignore byname)); require URI::Escape; my $user = URI::Escape::uri_escape(get_conf('cbstream', 'pmuser')) +; my $pass = URI::Escape::uri_escape(get_conf('cbstream', 'pmpasswor +d')); my $me = LWP::Simple::get("http://www.perlmonks.org/index.pl?op=lo +gin;user=$user;passwd=$pass;displaytype=xml;ticker=yes;node=$user"); my $twig = XML::Twig->new(); $twig->parse($me); my @elt = $twig->get_xpath('//var[@name="ignoredusers"]'); if (@elt) { my $ignored = $elt[0]->text(); my @uids = ($ignored =~ /\|(\d+),/g); my @users = map { my $nick = get_conf(qw(cbstream ignore byid), $_) || do { my $xml = LWP::Simple::get("http://www.perlmonks.org/i +ndex.pl?displaytype=xml;node_id=$_"); my $nicktwig = XML::Twig->new(); $nicktwig->parse($xml); (my $user = ($nicktwig->get_xpath('//author'))[0]->tex +t()) =~ s/^\s+//; $user =~ s/\s+$//; set_conf(qw(cbstream ignore byid), \$_, $user); $user; }; set_conf(qw(cbstream ignore byname), $nick, $_); $nick; } @uids; if (@users) { Xchat::print("Ignoring: @users"); } else { Xchat::print("Ignoring no one"); } save_conf(); } else { Xchat::print "no one being ignored (maybe failed to log in?)"; } Xchat::EAT_ALL; } Xchat::hook_command('cbignore', \&add_ignore); sub add_ignore { my $person = $_[1][1]; return Xchat::EAT_ALL unless $person; $person =~ s/^\[(.*)\]/$1/; Xchat::print "Adding [$person] to PM ignores"; Xchat::command "say /ignore [$person]"; set_conf(qw(cbstream ignore byname), $person, -1); Xchat::EAT_ALL; } Xchat::hook_command('cbunignore', \&rm_ignore); sub rm_ignore { my $person = $_[1][1]; return Xchat::EAT_ALL unless $person; $person =~ s/^\[(.*)\]/$1/; Xchat::print "Removing [$person] from PM ignores"; Xchat::command "say /unignore [$person]"; rm_conf(qw(cbstream ignore byname), $person); Xchat::EAT_ALL; } Xchat::hook_command('cbmsg', \&cb_msg); sub cb_msg { my $msg = shift; my $nth = shift; Xchat::print "Sending $nth->[1] (don't expect a response here)"; Xchat::command "say /msg $nth->[1]"; Xchat::EAT_ALL; } Xchat::hook_server('PRIVMSG', \&rewrite_cb, { priority => Xchat::PRI_L +OW }); sub rewrite_cb { my $msg = shift; my $nth = shift; if (Xchat::get_info('network') eq 'FreeNode') { if ($msg->[0] =~ /^:cbstream!/ and $msg->[1] eq 'PRIVMSG' and $msg->[2] eq '#cbstream' and 1 ) { my $prefix = get_conf('cbstream','nick-prefix') || ''; my $suffix = get_conf('cbstream','nick-suffix') || ''; (my $alias) = $nth->[3] =~ /^\S*\[(.*?)\]/; (my $safealias = $alias) =~ s/\s/_/g; (my $newmsg = $nth->[0]) =~ s/^:cbstream!/:$prefix$safeali +as$suffix!/; $newmsg =~ s/\[\Q$alias\E\]\s+//; # check if it's someone we think we're ignoring... if (! get_conf(qw(cbstream ignore byname), $alias) ) { # if it's an action, we need to convert it to such. $newmsg =~ s[:\+/me (.*)][:\001ACTION $1\001]; Xchat::command("recv $newmsg"); } elsif (my $mode = get_conf(qw(cbstream ignore_mode))) { if ($mode eq '1' or $mode eq 'brief') { Xchat::print("$alias said something, but we're ign +oring them."); } elsif ($mode eq '2' or $mode eq 'verbose') { $newmsg =~ s[^.*:\+][]; Xchat::print("$alias said '$newmsg', but we're ign +oring them."); } } Xchat::EAT_ALL; } } } Xchat::hook_command('cbset', \&cb_set); sub cb_set { my $msg = shift; my $nth = shift; set_conf('cbstream', @{$msg}[1..$#$msg]); Xchat::print ("set " . join(' ', map { "[$_]" } @{$msg}[1..$#$msg-1]) . " to {" . $msg->[-1] . "}"); save_conf(); Xchat::EAT_ALL; } Xchat::hook_command('cbget', \&cb_get); sub cb_get { my $msg = shift; my $nth = shift; my $val = get_conf('cbstream', @{$msg}[1..$#$msg]); if (defined $val) { if ($nth->[1] =~ /password/) { $val = '(not displayed)'; } else { $val = "{ $val }" } } else { $val = '<not defined>'; } Xchat::print(join(' ', map { "[$_]" } @{$msg}[1..$#$msg]) . " = $val"); Xchat::EAT_ALL; } Xchat::hook_command('cbrm', \&cb_rm); sub cb_rm { my $msg = shift; my $nth = shift; rm_conf('cbstream', @{$msg}[1..$#$msg]); Xchat::print('deleted ' . join(' ', map { "[$_]" } @{$msg}[1..$#$msg])); save_conf(); Xchat::EAT_ALL; }
As you may see, this uses YAML::Syck - I'm sure a few minor changes could get it to use YAML instead, or any other configuration you want. The configuration file, currently called "cbstream.yaml" in the same directory, looks like this:
--- cbstream: pmuser: Tanktalus pmpassword: somelongpasswordthatI'llneverforgetIhope
If you want to have a prefix in front of each alias (the #xchat gurus suggested "cb|" so you could tell the difference), you can add "  nick-prefix: cb|" at the end, and that should work. I had that for a while (well, I had it hard-coded), but eventually decided against it (so I made it optional).

Update 1: Fixed LeaveCBStreamLogin to not print out the :+ leading part of the message from cbstream acknowledging login (cosmetic), and allow user to specify prefix AND suffix for nicks. I've set this in my yaml file now:

nick-prefix: "[" nick-suffix: "]"
and nicks show up with the []'s, which makes it better for cut&paste, and also helps differentiate between what I send ("Tanktalus") and what I receive from cbstream ("[Tanktalus]").

Update 2: replaced with version 0.03: now with /cbig to load the ignores from perlmonks. Lots of new prereqs. And some POD to help.

Update 3: replaced with version 0.04: now with /cbignore and /cbunignore, and fix for Your Mother (and any other nicks with spaces).

Update 4: replaced with version 0.05: now with /cbset, /cbget, /cbrm for storing values from one invocation to another.

Update 5: replaced with version 0.06: now with /cbmsg.

Update 6: replaced with version 0.07: bug fixes (/cbmsg was causing Xchat to die, /cbig was broken).

Update 7: replaced with version 0.08: bug fix (cLive ;-) was causing a regex problem - fixed by using \Q...\E).

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://703918]
Approved by kyle
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (4)
As of 2024-03-19 10:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found