[Mailman-Users] fallen behind in moderating? handy script
Greg Stein
gstein at lyra.org
Sun May 15 03:13:44 CEST 2005
Oops. The message to mailman-developers was rejected. Maybe mailman-users
is more appropriate anyways...
----- Forwarded message from Greg Stein <gstein at lyra.org> -----
From: Greg Stein <gstein at lyra.org>
Subject: fallen behind in moderating? handy script
To: mailman-developers at python.org
Date: Sat, 14 May 2005 18:07:21 -0700
A number of lists on my mailman machine have fallen *way* behind in their
moderation queues. Once the queue gets to be too large (many hundreds),
the web form doesn't really work any more. But you still have to get the
spam crap outta there. And in an efficient manner.
I'm not sure it is in the latest distribution (seems like I recall it was
removed), but there is a bin/discard script that I find useful.
Previously, I would search all the heldmsg files for certain patterns and
pipe the results into xargs discard. Kind of like this:
$ cd install/data
$ ls | fgrep heldmsg | xargs fgrep -l -i mortgage | xargs ../bin/discard
That helped, but wasn't very efficient and it still leaves a ton of other
kinds of spam behind.
So... I wrote a little Python/curses app to quickly zoom through held
messages for a list. I *just* wrote this sucker this afternoon, and it has
very few comments, robustness, etc. But maybe somebody else can find it
useful, so I'm posting it here (attached).
I run it like this:
$ cd install/data
$ ../bin/manage LISTNAME
$ cat discard.LISTNAME | xargs ../bin/discard
The app presents a list of all the held messages down the left pane, and
the From/Subject/Body fields in the right pane. Hitting space bar toggles
the delete status and moves you to the next one. 'd' marks it for delete.
'u' undeletes it. 'p/UP moves up a file. 'n'/DOWN moves to the next file.
'q' will exit the app and write out the discard.LISTNAME file. That's
about it.
Enjoy!
Cheers,
-g
--
Greg Stein, http://www.lyra.org/
----- End forwarded message -----
--
Greg Stein, http://www.lyra.org/
-------------- next part --------------
#!/usr/local/bin/python
import sys
import cPickle
import os
import re
import curses
import paths
cre = re.compile(r'heldmsg-(?P<listname>.*)-(?P<id>[0-9]+)\.(pck|txt)$')
def usage(code=1):
print 'USAGE: %s LISTNAME' % sys.argv[0]
sys.exit(code)
def main():
if len(sys.argv) != 2:
usage()
listname = sys.argv[1]
messages = [ ]
for fname in os.listdir(os.path.join(paths.prefix, 'data')):
m = cre.match(fname)
if m and m.group(1) == listname:
# message number, filename, flagged for discard
messages.append([int(m.group(2)), fname, False])
messages.sort()
curses.wrapper(manage, messages)
f = open('discard.' + listname, 'w')
for idx, fname, flag in messages:
if flag:
f.write(fname + '\n')
def manage(main_win, messages):
m_height, m_width = main_win.getmaxyx()
# curses.use_default_colors()
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
bg = curses.color_pair(1)
main_win.bkgd(ord(' '), bg)
for i in range(m_height):
main_win.addstr(i, 0, ' ' * (m_width - 1))
main_win.redrawwin()
main_win.vline(0, 8, curses.ACS_VLINE, m_height)
header_win = main_win.derwin(1, 8, 0, 0)
header_win.syncok(True)
header_win.addstr(0, 0, 'Files', curses.A_BOLD)
files_win = main_win.derwin(m_height - 1, 8, 1, 0)
files_win.syncok(True)
from_win = main_win.derwin(1, m_width - 9, 0, 9)
from_win.syncok(True)
from_win.addstr(0, 0, 'From:', curses.A_BOLD)
subject_win = main_win.derwin(1, m_width - 9, 1, 9)
subject_win.syncok(True)
subject_win.addstr(0, 0, 'Subject:', curses.A_BOLD)
body_win = main_win.derwin(m_height - 2, m_width - 9, 2, 9)
body_win.syncok(True)
list_offset = 0
position = 0
while 1:
files_win.erase()
for i in range(min(m_height - 1, len(messages) - list_offset)):
files_win.addstr(i, 2, str(messages[i + list_offset][0]))
if messages[i + list_offset][2]:
files_win.addch(i, 0, ord('*'))
msg = cPickle.load(open(messages[position][1]))
from_win.move(0, 9)
from_win.clrtoeol()
from_win.addstr(get_header(msg, 'from', m_width - 10 - 9))
subject_win.move(0, 9)
subject_win.clrtoeol()
subject_win.addstr(get_header(msg, 'subject', m_width - 10 - 9))
body_win.erase()
while msg.is_multipart():
msg = msg.get_payload(0)
body = msg.get_payload(decode=True)
lines = body.splitlines()
for i in range(min(m_height - 2, len(lines))):
body_win.addstr(i, 0, lines[i][:m_width - 1 - 9].translate(table))
main_win.refresh()
ch = main_win.getch(1 + position - list_offset, 0)
if ch == ord('q'):
return
if ch == 12: # ctrl-l
main_win.redrawwin()
main_win.touchwin()
elif ch == ord(' '):
messages[position][2] = not messages[position][2]
position = position + 1
elif ch == ord('d'):
messages[position][2] = True
position = position + 1
elif ch == ord('u'):
messages[position][2] = False
position = position + 1
elif ch in (curses.KEY_UP, ord('p')):
position = position - 1
elif ch in (curses.KEY_DOWN, ord('n')):
position = position + 1
# bound the cursor to the size of the list.
if position < 0:
position = 0
elif position >= len(messages):
position = len(messages) - 1
# scroll the file list if necessary
if position < list_offset:
list_offset = list_offset - (m_height - 4)
if list_offset < 0:
list_offset = 0
elif position - list_offset > m_height - 2:
list_offset = list_offset + (m_height - 4)
def get_header(msg, header, clip):
s = msg[header]
if s is None:
return '<not present>'
return s[:clip].translate(table)
table = ''.join([(c < 32 or c > 127) and '?' or chr(c) for c in range(256)])
if __name__ == '__main__':
main()
More information about the Mailman-Users
mailing list