[slrn] Cleanthreads v.2



I've improved my program for tagging retrieved articles ( using slrnpull ) so
that it now identifies both new article bodies and new headers. It does so by
reading slrns' newsrc file after calling slrn. The call to slrn now not only
makes sure the tagged articles are safe from deletion, but because it writes
the newsrc file, after the call it can be read to identify which articles are
left on the list. So now the overall deletion of incomplete threads occurs
only whenever slrnpull is called, not each time a newsgroup is entered. The
call to slrn requires a copy of the initialization file ( .slrnrc ) with the
following interpret lines:

interpret groups.sl
interpret cleaner.sl

and the cleaner macro only has the delete routine:

() = register_hook ("article_mode_startup_hook", "my_groups" );
define make_clean_threads ()
{
call ("header_bob");
do
{
if (not ("" == extract_article_header ("References")))
{
if (1 == get_body_status ())
{
call ("delete");
header_up (1);
}
}
}
while (header_down (1));
call ("expunge");
}
() = register_hook ("article_mode_startup_hook", "make_clean_threads" );

The groups macro is written by the Icon program, which also writes the
mythreads macro based on information it receives after the call to slrn. So
the ordinary use .slrnrc file should have both of these interpret lines:

interpret mythreads.sl
interpret cleanthreads.sl

The cleanthreads macro registers the routines defined in mythreads and
contains other routines not strictly related to this program.

procedure main( x )
v := []
d := []
e := []
k := []
v1 := 0
b6 := table()
n7 := []
s := open( "/home/roy/spool/slrnpull.conf" ) | stop( "no slrnpull conf file" )
while s1 := read( s ) do {
if s1[ 1 ] == "#" then next
s1 ? {
s2 := tab( upto( ' ' ) | 0 )
if s2 == ( "default" | "" ) then next
else put( d, s2 )
} }
x[ 1 ] | system( "slrnpull -d /home/roy/spool --fetch-score 1" ) | exit()
every put( v, m1( !d ) )
if *v > 0 then {
m := open( "groups.sl", "crw" )
write( m, "define my_groups ()\n{ variable mm = current_newsgroup (),dma,hhh,list;" )
every b := !v do {
v1 +:= 1
b1 := b[ 1 ]
b2 := b[ 2 ]
b3 := b[ 3 ]
write( m, "variable " || b[ 6 ] || "_threads = {" )
every u := open( b3 || "/" || ( b4 := !b1 ), "r" ) do
{
b2 -:= 1
while c := read( u ) do {
( c[ 1:9 ] == "Message-" ) & break
}
writes( m, "\"", ( b5 := c[ 13:0 ] ), "\"" )
if b2 > 0 then write( m, "," )
b6[ b4 ] := b5
close( u )
}
write( m, " };" )
b[ 1 ] := b6
v[ v1 ] := b
put( e, b[ 5:0 ] )
b6 := table()
}
write( m, "switch (mm)" )
every e1 := !e do {
write( m, "{ case \"", e1[ 1 ], "\" : list = ", e1[ 2 ] || "_threads; }" )
}
write( m, "{ list = {}; }\nhhh = length (list);\nwhile (hhh)\n{ hhh--;\n",
"dma = list[hhh];\nif (1 == locate_header_by_msgid (dma,0))\n",
"if (0 == get_body_status ())\nset_header_flags (4); }\n",
"collapse_threads (); }\n",
"define group_mode_startup_hook ()\n{variable dma, hhh, news = {" )
while e2 := get( e ) do {
writes( m, "\"", !e2, "\"" )
if *e > 0 then write( m, "," ) }
write( m, " };\nhhh = length (news);\nwhile (hhh)\n{ hhh--;\ndma = news[hhh];\n",
"set_group_order (dma);\ngroup_up_n (1);\nif (dma == current_newsgroup ())\n",
"{select_group ();\ncall(\"quit\");}}\ncall(\"quit\");}" )
system( "slrn -n -w0 -i groupmoderc --debug slrnfile" )
}
else exit()
n := ( open( ".jnewsrc" ) )
while n1 := read( n ) do {
n1 ? {
n2 := tab( upto( ':' ) )
move( 2 )
repeat {
n3 := tab( upto( ',' ) | 0 )
if numeric( n3 ) then {
put( k, integer( n3 ) )
move( 1 ) | break
next
}
n3 ? {
k1 := integer( tab( upto( '-' ) ) )
move( 1 )
k2 := integer( tab( 0 ) )
put( k, k1 )
while k1 +:= 1 do {
put( k, k1 )
if k1 = k2 then break
}
}
move( 1 ) | break }
}
v1 := 0
every b := !v do {
v1 +:= 1
if b[ 5 ] == n2 then {
n4 := b[ 4 ] -- set( k )
break
}
}
every put( n7, key( b[ 1 ] ) )
n5 := set( n7 ) ** n4
n6 := *n5
if n6 > 0 then {
b[ 3 ] := n5
b[ 2 ] := n6
v[ v1 ] := b }
else v := v[ 1 : v1 ] ||| v[ (v1 + 1):0 ]
k := []
n7 := []
}
o := open( "mythreads.sl", "crw" )
write( o, "define my_groups ()\n{ variable mm = current_newsgroup (),dma,hhh,list;" )
every b := !v do {
b1 := b[ 1 ]
b2 := b[ 2 ]
b3 := b[ 3 ]
write( o, "variable " || b[ 6 ] || "_threads = {" )
every u := b1[ !b3 ] do
{
b2 -:= 1
writes( o, "\"", u, "\"" )
if b2 > 0 then write( o, "," )
}
write( o, " };" )
put( e, b[ 5:0 ] )
}
write( o, "switch (mm)" )
every e1 := !e do {
write( o, "{ case \"", e1[ 1 ], "\" : list = ", e1[ 2 ] || "_threads; }" )
}
write( o, "{ list = {}; }\nhhh = length (list);\nwhile (hhh)\n{ hhh--;\n",
"dma = list[hhh];\nif (1 == locate_header_by_msgid (dma,0))\n",
"set_header_score (1); }\ncall(\"header_bob\"); }\n",
"define group_mode_startup_hook ()\n{variable dma, hhh, news = {" )
while e2 := get( e ) do {
writes( o, "\"", !e2, "\"" )
if *e > 0 then write( o, "," ) }
write( o, " };\nhhh = length (news);\nwhile (hhh)\n{ hhh--;\ndma = news[hhh];\n",
"set_group_order (dma);\ngroup_up_n (1);\nset_group_flags (2); } }" )
end
procedure m1( s )
s2 := ""
f := "/home/roy/spool/news/"
g := ""
g1 := "/home/roy/clean/"
k := []
g6 := []
g7 := []
g8 := []
s ? { while s1 := tab( upto( '.' ) ) do {
f ||:= s1 || "/"
s2 ||:= s1[ 1:2 ]
move( 1 ) }
s3 := tab( 0 )
f ||:= s3
s2 ||:= s3[ 1:4 ]
every g ||:= ( !s2 ** &letters )
}
g2 := g1 || g || "-spool"
g3 := open( g2, "rw" | "crw" )
g4 := g1 || g || "-allbodies"
g5 := open( g4, "rw" | "crw" )
while put( g6, integer( read( g3 ) ) )
while put( g7, integer( read( g5 ) ) )
close( g3 )
system( "ls " || f || " > " || g2 ) | stop( "unable to open directory" )
g3 := open( g2, "rw" )
while put( g8, integer( read( g3 ) ) )
g9 := set( g8 )
# find new headers
g01 := g9 -- set( g6 )
n := ( open( f || "/.headers" ) | "" )
n1 := !n
\n1 ? {
repeat {
n2 := tab( upto( ',' ) | 0 )
if numeric( n2 ) then {
put( k, integer( n2 ) )
move( 1 ) | break
next
}
n2 ? {
k1 := integer( tab( upto( '-' ) ) )
move( 1 )
k2 := integer( tab( 0 ) )
put( k, k1 )
while k1 +:= 1 do {
put( k, k1 )
if k1 = k2 then break
}
}
move( 1 ) | break }
}
g02 := g9 -- set( k )
# find new bodies
g03 := g02 -- set( g7 )
g04 := g01 ++ g03
h := *g04
g05 := g1 || g || "-bodies"
g06 := open( g05, "crw" )
every write( g06, !g02 )
close( g06 )
close( g5 )
system( "mv " || g05 || " " || g4 )
if ( h > 0 ) & ( *g7 > 0 ) & ( *g6 > 0 ) then return [ g04, h, f, g9, s, g ]
end

To use this, edit the four lines containing addresses on your system, that is:
1. the location of the slrnpull conf. file
2. the location of the slrnpull news directory
3. the slrnpull command line
4. the location of where this program will keep its files
Then compile it with Icon ( http://cs.arizona.edu/icon ). The first time it
is used, it will only gather information about the article bodies present in
the spool, so you can obviate the call to slrnpull by calling this program
with an argument (anything), unless of course the group is totally new and you
don't have a directory already created for it in the slrnpull news directory.
Here is what my current mythreads.sl macro looks like:

define my_groups ()
{ variable mm = current_newsgroup (),dma,hhh,list;
variable aisuf_threads = {
"<3ba6225a-3939-43b1-a698-cd1e8c30d5de@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<44dcfb12-58c4-482e-8861-eccb6841a04c@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>" };
variable asuf_threads = {
"<488bfd86-2288-4238-91fc-abe3b2db710e@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<bbd5f963-a11d-469b-8bad-4a88017de2ff@xxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<e05cfd9f-3f53-4c06-bb58-6f1e7feb2032@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>" };
variable aath_threads = {
"<i4ta2o$8dg$1@xxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<Virgil-5FF374.17373022082010@xxxxxxxxxxxxxxxxxxxxxxxxx>",
"<39b4eede-9716-484f-8fc9-b4423153ac49@xxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<TUnco.68141$lS1.58138@xxxxxxxxxxxx>",
"<172fb4c8-4f00-412c-b395-dbacb0c78033@xxxxxxxxxxxxxxxxxxxxxxxxxxx>" };
variable apsy_threads = {
"<40ef6d88-d80a-4155-a9c9-edc8d03e7d42@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<c3b6aedb-39b7-4a6b-b978-336e5cc4873c@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>" };
variable arccat_threads = {
"<285a268f-f929-4896-b8cd-13deff5961c4@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<8dettmF4d3U1@xxxxxxxxxxxxxxxxxx>" };
variable trnew_threads = {
"<NMudnZnZAMQX4-_RnZ2dnUVZ_sOdnZ2d@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<lNOdnZACK_U-4O_RnZ2dnUVZ_uudnZ2d@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>" };
variable trcou_threads = {
"<26fbbd39-52d0-44fa-9cc0-c92766e2b53a@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>",
"<ZABco.58710$F%7.3802@xxxxxxxxxxxx>",
"<S2zco.28345$6o7.12482@xxxxxxxxxxxx>" };
variable ecmat_threads = {
"<8df3t2F82eU1@xxxxxxxxxxxxxxxxxx>",
"<8dfbp1FnfpU1@xxxxxxxxxxxxxxxxxx>" };
variable ehlit_threads = {
"<8dfsq9F3g3U1@xxxxxxxxxxxxxxxxxx>",
"<8dfs8lFvcjU1@xxxxxxxxxxxxxxxxxx>",
"<i4ubig$kfc$1@xxxxxxxxxxxxxxxxx>" };
switch (mm)
{ case "alt.islam.sufism" : list = aisuf_threads; }
{ case "alt.sufi" : list = asuf_threads; }
{ case "alt.atheism" : list = aath_threads; }
{ case "alt.psychology" : list = apsy_threads; }
{ case "alt.religion.christian.catholic" : list = arccat_threads; }
{ case "talk.religion.newage" : list = trnew_threads; }
{ case "talk.religion.course-miracle" : list = trcou_threads; }
{ case "es.ciencia.matematicas" : list = ecmat_threads; }
{ case "es.humanidades.literatura" : list = ehlit_threads; }
{ list = {}; }
hhh = length (list);
while (hhh)
{ hhh--;
dma = list[hhh];
if (1 == locate_header_by_msgid (dma,0))
set_header_score (1); }
call("header_bob"); }
define group_mode_startup_hook ()
{variable dma, hhh, news = {
"alt.islam.sufism",
"alt.sufi",
"alt.atheism",
"alt.psychology",
"alt.religion.christian.catholic",
"talk.religion.newage",
"talk.religion.course-miracle",
"es.ciencia.matematicas",
"es.humanidades.literatura" };
hhh = length (news);
while (hhh)
{ hhh--;
dma = news[hhh];
set_group_order (dma);
group_up_n (1);
set_group_flags (2); } }

And here are the relevant parts from cleanthreads.sl:

() = register_hook ("article_mode_hook", "my_groups" );
define make_clean_threads ()
{
variable amd, dma, hhh, list, mdo, ppp;
list = {};
call ("header_bob");
do
{
if (not ("" == extract_article_header ("References")))
{
amd = extract_article_header ("Message-ID");
list_insert (list, amd);
}
}
while (header_down (1));
hhh = length (list);
mdo = hhh;
while (mdo)
{
mdo--;
dma = list[mdo];
() = locate_header_by_msgid (dma,0);
set_prefix_argument (2);
call ("get_parent_header");
}
collapse_threads ();
if (hhh == 0)
call("header_bob");
}

% I've found these routines useful for keeping threads collapsed when
% reading the original post, reading read articles, etc.

define going_down ()
{
header_down (1);
if (1 == has_parent ())
article_line_down (1);
else
{
article_line_down (1);
collapse_thread ();
}
}
define going_up ()
{
header_up (1);
if (1 == has_parent ())
article_line_down (1);
else
{
article_line_down (1);
collapse_thread ();
}
}
define prevheader ()
{
if (1 == prev_tagged_header ())
{
if (0 == get_body_status ())
article_line_down (1);
if (0 == has_parent ())
collapse_threads ();
}
}
define new_slash ()
{
if (1 == has_parent ())
call("hide_article");
else
{
call("hide_article");
collapse_thread ();
}
}
define nextheader ()
{
if (1 == next_tagged_header ())
{
if (0 == get_body_status ())
article_line_down (1);
if (0 == has_parent ())
collapse_threads ();
}
}
define my_down ()
{
variable amd;
if (not (3 == _is_article_visible ()))
article_line_down ();
else
{
amd = article_cline_number ();
amd++;
article_goto_line (amd);
}
if (0 == has_parent ())
collapse_thread ();
}
define my_up ()
{
variable amd;
if (not (3 == _is_article_visible ()))
article_line_up ();
else
{
amd = article_cline_number ();
amd--;
article_goto_line (amd);
}
if (0 == has_parent ())
collapse_thread ();
}
define recon_old ()
{
() = register_hook ("article_mode_startup_hook", "make_clean_threads");
select_group ();
() = unregister_hook ("article_mode_startup_hook", "make_clean_threads");
}
definekey ("going_down", "<Right>", "article");
definekey ("going_up", "<Left>", "article");
definekey ("new_slash", "/", "article" );
definekey ("nextheader", "7", "article" );
definekey ("prevheader", "6", "article" );
definekey ("my_down", "<Down>", "article" );
definekey ("my_up", "<Up>", "article" );
definekey ("recon_old", " ", "group" );


--
"The world of existence is an emanation of the merciful attribute of God."
Abdu'l-Baha
http://www.costarricense.cr/pagina/ernobe
.



Relevant Pages

  • Re: [slrnpull] Logging of retrieved articles?
    ... Here's an expanded version of it, which includes code that will write slrn ... that have article bodies downloaded in accordance with the slrnpull score ... signal that it is one of the groups that has tagged articles. ... nothing stopping the cleanthreads macro from deleting it and the ...
    (news.software.readers)
  • Re: slrnpull: cannot get it to work after changing news server (to free.teranews.com)
    ... I want all available articles by default. ... I tried that to have a look at what slrnpull would do. ... I really didn't want to dig into slrn problems due to slrnpull. ... I read your original post ...
    (news.software.readers)
  • [slrn] slrn loops whilst entering newsgroup
    ... I'm having a recurring problem with slrn and slrnpull pre1.0.0-2. ... article count and into the loop when I try to enter the group. ... limit the number of articles. ...
    (news.software.readers)
  • [New Slrn/Slrnpull] Scoring
    ... and those articles are left on the server. ... the commandline when you call slrnpull) in SLRNPULL_ROOT/score. ... But the slrnpull score file must be edited by hand, ... The solution I've come up with is to make sure that the slrn ...
    (news.software.readers)
  • Re: [slrnpull] slrn macro to download new messages
    ... Troy Piggins Wrote in news.software.readers: ... I have just started using slrnpull again now ... just wrote a macro so that with one keypress ... which I normally do as a cronjob. ...
    (news.software.readers)