1 # Copyright (C) 1998-2008 by the Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 """Handler for auto-responses."""
21 __all__
= ['Replybot']
28 from string
import Template
29 from zope
.interface
import implements
31 from mailman
import Message
32 from mailman
import Utils
33 from mailman
.i18n
import _
34 from mailman
.interfaces
import IHandler
37 log
= logging
.getLogger('mailman.error')
38 NODELTA
= datetime
.timedelta()
42 def process(mlist
, msg
, msgdata
):
43 # Normally, the replybot should get a shot at this message, but there are
44 # some important short-circuits, mostly to suppress 'bot storms, at least
45 # for well behaved email bots (there are other governors for misbehaving
46 # 'bots). First, if the original message has an "X-Ack: No" header, we
47 # skip the replybot. Then, if the message has a Precedence header with
48 # values bulk, junk, or list, and there's no explicit "X-Ack: yes" header,
49 # we short-circuit. Finally, if the message metadata has a true 'noack'
50 # key, then we skip the replybot too.
51 ack
= msg
.get('x-ack', '').lower()
52 if ack
== 'no' or msgdata
.get('noack'):
54 precedence
= msg
.get('precedence', '').lower()
55 if ack
<> 'yes' and precedence
in ('bulk', 'junk', 'list'):
57 # Check to see if the list is even configured to autorespond to this email
58 # message. Note: the mailowner script sets the `toadmin' or `toowner' key
59 # (which for replybot purposes are equivalent), and the mailcmd script
60 # sets the `torequest' key.
61 toadmin
= msgdata
.get('toowner')
62 torequest
= msgdata
.get('torequest')
63 if ((toadmin
and not mlist
.autorespond_admin
) or
64 (torequest
and not mlist
.autorespond_requests
) or \
65 (not toadmin
and not torequest
and not mlist
.autorespond_postings
)):
67 # Now see if we're in the grace period for this sender. graceperiod <= 0
68 # means always autorespond, as does an "X-Ack: yes" header (useful for
70 sender
= msg
.get_sender()
72 graceperiod
= mlist
.autoresponse_graceperiod
73 if graceperiod
> NODELTA
and ack
<> 'yes':
75 quiet_until
= mlist
.admin_responses
.get(sender
, 0)
77 quiet_until
= mlist
.request_responses
.get(sender
, 0)
79 quiet_until
= mlist
.postings_responses
.get(sender
, 0)
82 # Okay, we know we're going to auto-respond to this sender, craft the
83 # message, send it, and update the database.
84 realname
= mlist
.real_name
86 'Auto-response for your message to the "$realname" mailing list')
87 # Do string interpolation into the autoresponse text
88 d
= dict(listname
= realname
,
89 listurl
= mlist
.script_url('listinfo'),
90 requestemail
= mlist
.request_address
,
91 owneremail
= mlist
.owner_address
,
94 rtext
= mlist
.autoresponse_admin_text
96 rtext
= mlist
.autoresponse_request_text
98 rtext
= mlist
.autoresponse_postings_text
99 # Interpolation and Wrap the response text.
100 text
= Utils
.wrap(Template(rtext
).safe_substitute(d
))
101 outmsg
= Message
.UserNotification(sender
, mlist
.bounces_address
,
102 subject
, text
, mlist
.preferred_language
)
103 outmsg
['X-Mailer'] = _('The Mailman Replybot')
104 # prevent recursions and mail loops!
105 outmsg
['X-Ack'] = 'No'
107 # update the grace period database
108 if graceperiod
> NODELTA
:
109 # graceperiod is in days, we need # of seconds
110 quiet_until
= now
+ graceperiod
* 24 * 60 * 60
112 mlist
.admin_responses
[sender
] = quiet_until
114 mlist
.request_responses
[sender
] = quiet_until
116 mlist
.postings_responses
[sender
] = quiet_until
121 """Send automatic responses."""
126 description
= _('Send automatic responses.')
128 def process(self
, mlist
, msg
, msgdata
):
129 """See `IHandler`."""
130 process(mlist
, msg
, msgdata
)