From 7ce72032518e164fbcef80ed0c3b20b91a1a9d26 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Wed, 16 Aug 2000 00:49:39 +0000 Subject: [PATCH] Summoning. --- .cvsignore | 1 + Makefile | 7 ++++ bot.tcl | 98 ++++++++++++++++++++++++++++++++++++++++++------------ summon-write.pl | 38 +++++++++++++++++++++ summon.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 21 deletions(-) create mode 100644 .cvsignore create mode 100644 Makefile create mode 100755 summon-write.pl create mode 100644 summon.c diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..170a88f --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +summon diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..96678bc --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +# + +CFLAGS= -Wall -Wwrite-strings -Wmissing-prototypes \ + -Wstrict-prototypes -Wpointer-arith \ + -O2 -g + +all: summon diff --git a/bot.tcl b/bot.tcl index 8d2083a..54cda63 100755 --- a/bot.tcl +++ b/bot.tcl @@ -2,7 +2,7 @@ set host chiark set port 6667 -set nick Blight +if {![info exists nick]} { set nick Blight } proc sendout {command args} { global sock @@ -49,6 +49,7 @@ proc onread {args} { global sock if {[gets $sock line] == -1} { set terminate 1; return } +binary scan $line H* inhex; puts >$inhex< regsub -all "\[^ -\176\240-\376\]" $line ? line set org $line if {[regexp -nocase {^:([^ ]+) (.*)} $line dummy prefix remain]} { @@ -92,13 +93,12 @@ proc onread {args} { } } -proc msendprivmsg {dest ll} { - foreach l $ll { sendout PRIVMSG $dest $l } -} - -proc msendprivmsg_delayed {delay dest ll} { - after $delay [list msendprivmsg $dest $ll] +proc sendprivmsg {dest l} { + sendout [expr {[ischan $dest] ? "PRIVMSG" : "NOTICE"}] $dest $l } +proc sendaction {dest what} { sendout PRIVMSG $dest "\001ACTION $what\001" } +proc msendprivmsg {dest ll} { foreach l $ll { sendprivmsg $dest $l } } +proc msendprivmsg_delayed {delay dest ll} { after $delay [list msendprivmsg $dest $ll] } proc prefix_none {} { upvar 1 p p @@ -138,11 +138,9 @@ proc prefix_nick {} { if {"[irctolower $n]" == "[irctolower $nick]"} { error "from myself" } } -proc showinterval {howlong} { - if {$howlong <= 0} { - return {just now} - } elseif {$howlong < 1000} { - return "${howlong}s ago" +proc showintervalsecs {howlong} { + if {$howlong < 1000} { + return "${howlong}s" } else { if {$howlong < 1000000} { set pfx k @@ -154,11 +152,19 @@ proc showinterval {howlong} { set value [expr "$howlong.0 / $scale"] foreach {min format} {100 %.0f 10 %.1f 1 %.2f} { if {$value < $min} continue - return [format "$format${pfx}s ago" $value] + return [format "$format${pfx}s" $value] } } } +proc showinterval {howlong} { + if {$howlong <= 0} { + return {just now} + } else { + return "[showintervalsecs $howlong] ago" + } +} + proc showtime {when} { return [showinterval [expr {[clock seconds] - $when}]] } @@ -264,7 +270,7 @@ proc chanmode_o1 {m g p chan} { set who [chanmode_arg] recordlastseen_n $n "being nice to $who" 1 if {"[irctolower $who]" == "[irctolower $nick]"} { - sendout PRIVMSG $n Thanks. + sendprivmsg $n Thanks. } } @@ -326,11 +332,17 @@ proc msg_PRIVMSG {p c dest text} { } $procname $p $dest } rv]} { - sendout PRIVMSG $n "error: $rv" + sendprivmsg $n "error: $rv" } else { - foreach {td val} [list $n [lindex $rv 0] $output [lindex $rv 1]] { + manyset $rv priv_msgs pub_msgs priv_acts pub_acts + foreach {td val} [list $n $priv_msgs $output $pub_msgs] { + foreach l [split $val "\n"] { + sendprivmsg $td $l + } + } + foreach {td val} [list $n $priv_acts $output $pub_acts] { foreach l [split $val "\n"] { - sendout PRIVMSG $td $l + sendaction $td $l } } } @@ -360,16 +372,17 @@ proc def_ucmd {cmdname body} { proc ucmd/$cmdname {p dest} " upvar 1 text text\n$body" } -proc ucmdr {priv pub} { - return -code return [list $priv $pub] +proc ucmdr {priv pub args} { + return -code return [concat [list $priv $pub] $args] } def_ucmd help { ta_nomore ucmdr \ {Commands currently understood: -help get this list of commands -seen ask after someone (I'll tell them you asked)} {} +help get this list of commands +seen ask after someone (I'll tell them you asked) +summon invite a logged-on user onto IRC} {} } proc manyset {list args} { @@ -379,6 +392,49 @@ proc manyset {list args} { } } +def_ucmd summon { + set target [ta_word] + ta_nomore + if { + [string length $target] > 8 || + [regexp {[^-0-9a-z]} $target] || + ![regexp {^[a-z]} $target] + } { error "invalid username" } + prefix_nick + + upvar #0 lastsummon($target) ls + set now [clock seconds] + if {[info exists ls]} { + set interval [expr {$now - $ls}] + if {$interval < 30} { + ucmdr {} \ + "Please be patient; $target was summoned only [showinterval $interval]." + } + } + regsub {^[^!]*!} $p {} path + if {[catch { + exec userv --timeout 3 $target irc-summon $n $path \ + [expr {[ischan $dest] ? "$dest" : ""}] \ + < /dev/null + } rv]} { + regsub -all "\n" $rv { / } rv + error $rv + } + if {[regexp {^problem (.*)} $rv dummy problem]} { + ucmdr {} "$target $problem." + } elseif {[regexp {^ok ([^ ]+) ([0-9]+)$} $rv dummy tty idlesince]} { + set idletime [expr {$now - $idlesince}] + set ls $now + ucmdr {} {} {} "invites $target ($tty[expr { + $idletime > 10 ? ", idle for [showintervalsecs $idletime]" : "" + }]) to [expr { + [ischan $dest] ? "join us here" : "talk to you" + }]." + } else { + error "unexpected response from userv service: $rv" + } +} + def_ucmd seen { global lastseen nick prefix_nick diff --git a/summon-write.pl b/summon-write.pl new file mode 100755 index 0000000..143d940 --- /dev/null +++ b/summon-write.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl + +sub fault ($) { print STDERR "$_[0]\n"; exit 8; } + +@ARGV==5 or fault("invalid arguments to $0"); +($line,$idlesince,$nick,$path,$chan) = @ARGV; + +open X, ">&0" or fault("reopen stdin for output: $!"); + +$timestring= localtime time; + +pline("\a"); +pline("You are cordially invited onto the system's IRC server by \`$nick'"); +pline("($path) at $timestring"); +pline("If you don't know to use IRC, run \`irc chiark'"); +pline("When you are on-line, please ". + (length $chan ? "join $chan." : "send a private note to $nick.")); +pline(""); + +close X or outfail(); + +print "ok $line $idlesince\n"; +exit 0; + +sub pline ($) { + my ($s) = @_; + print X "\r", " "x78, "\r $s\r\n" or outfail(); +} + +sub outfail () { + if ($! == &EAGAIN || $! == &EWOULDBLOCK) { + print "failed has a congested terminal\n"; + exit 0; + } else { + print "failed has a broken terminal ($!)\n"; + exit 0; + } +} diff --git a/summon.c b/summon.c new file mode 100644 index 0000000..d5e7989 --- /dev/null +++ b/summon.c @@ -0,0 +1,101 @@ +/* + * usage: + * .../summon + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void check(const char *string, const char *what, int maxlen) { + int c; + + if (strlen(string) > maxlen) { fprintf(stderr,"%s too long\n",what); exit(8); } + while ((c= *string++)) { + if (isspace((unsigned char)c) || !isprint((unsigned char)c)) + { fprintf(stderr,"bad char in %s\n",what); exit(8); } + } +} + +static void die(const char *msg) { + fprintf(stderr,"%s\n",msg); exit(8); +} + +static void problem(const char *msg) { + printf("problem %s\n",msg); + exit(0); +} + +enum howbad { hb_notlogon, hb_noterminal, hb_nomessages, hb_ok }; +static enum howbad closest= hb_notlogon; + +static time_t best_idle; +static char best_line[UT_LINESIZE+1]; + +static void thisis(enum howbad hb) { + if (hb > closest) closest= hb; +} + +int main(int argc, const char *const *argv) { + struct utmp *ue; + const char *myself; + struct stat stab; + int fd; + char idlebuf[20]; + + if (argc != 5) die("bad usage"); + + myself= getenv("USER"); + if (!myself) die("USER not set"); + if (strlen(myself) > UT_NAMESIZE) die("own username too long"); + + check(argv[2],"nick",20); + check(argv[3],"path",60); + check(argv[4],"channel",20); + + if (chdir("/dev")) { perror("chdir /dev"); exit(8); } + + while ((errno=0, ue= getutent())) { + if (ue->ut_type != USER_PROCESS) continue; + if (strncmp(ue->ut_user,myself,UT_NAMESIZE)) continue; + if (!ue->ut_line[0]) { thisis(hb_noterminal); continue; } + ue->ut_line[UT_LINESIZE]= 0; /* overflows into next field :-/ */ + if (stat(ue->ut_line,&stab)) { + printf("warning could not stat %s: %s\n",ue->ut_line,strerror(errno)); + thisis(hb_noterminal); continue; + } + if (!(stab.st_mode & S_IWGRP)) { thisis(hb_nomessages); continue; } + closest= hb_ok; + if (closest == hb_ok && stab.st_atime <= best_idle) continue; + strcpy(best_line,ue->ut_line); + best_idle= stab.st_atime; + } + if (errno) { perror("getutent set errno"); exit(8); } + + switch (closest) { + case hb_notlogon: problem("is not logged on"); + case hb_noterminal: problem("does not have any terminal/shell sessions"); + case hb_nomessages: problem("is refusing messages"); + case hb_ok: break; + default: abort(); + } + + sprintf(idlebuf,"%lu",(unsigned long)best_idle); + + fd= open(best_line, O_NOCTTY|O_NONBLOCK|O_WRONLY); + if (fd < 0) { + fprintf(stderr,"unable to open terminal (%s): %s",best_line,strerror(errno)); + exit(8); + } + if (dup2(fd,0)) { perror("dup2"); exit(8); } + execlp(argv[1],argv[1], best_line,idlebuf,argv[2],argv[3],argv[4], (const char*)0); + perror(argv[1]); exit(8); +} -- 2.11.0