------------------------------------------------------------
revno: 6695
committer: Barry Warsaw <[email protected]>
branch nick: hacking
timestamp: Thu 2009-02-19 20:46:57 -0500
message:
Complete the porting of the autoresponse implementation, with no need of the
old pickle attributes.
Fix a typo in the datetime.py module.
modified:
src/mailman/pipeline/docs/replybot.txt
src/mailman/pipeline/replybot.py
src/mailman/utilities/datetime.py
=== modified file 'src/mailman/pipeline/docs/replybot.txt'
--- src/mailman/pipeline/docs/replybot.txt 2009-02-13 01:36:21 +0000
+++ src/mailman/pipeline/docs/replybot.txt 2009-02-20 01:46:57 +0000
@@ -2,9 +2,9 @@
==================
Mailman has an auto-reply handler that sends automatic responses to messages
-it receives on its posting address, or special robot addresses. Automatic
-responses are subject to various conditions, such as headers in the original
-message or the amount of time since the last auto-response.
+it receives on its posting address, owner address, or robot address.
+Automatic responses are subject to various conditions, such as headers in the
+original message or the amount of time since the last auto-response.
>>> mlist = create_list(u'[email protected]')
>>> mlist.real_name = u'XTest'
@@ -16,19 +16,20 @@
[]
-Basic autoresponding
---------------------
+Basic automatic responding
+--------------------------
-Basic autoresponding occurs when the list is set up to respond to either its
--owner address, its -request address, or to the posting address, and a message
-is sent to one of these addresses. A mailing list also has an autoresponse
-grace period which describes how much time must pass before a second response
-will be sent, with 0 meaning "there is no grace period".
+Basic automatic responding occurs when the list is set up to respond to either
+its -owner address, its -request address, or to the posting address, and a
+message is sent to one of these addresses. A mailing list also has an
+automatic response grace period which specifies how much time must pass before
+a second response will be sent, with 0 meaning "there is no grace period".
>>> import datetime
>>> mlist.autorespond_admin = True
>>> mlist.autoresponse_graceperiod = datetime.timedelta()
>>> mlist.autoresponse_admin_text = u'admin autoresponse text'
+
>>> msg = message_from_string("""\
... From: [email protected]
... To: [email protected]
@@ -36,20 +37,25 @@
... help
... """)
+The preceding message to the mailing list's owner will trigger an automatic
+response.
+
>>> from mailman.pipeline.replybot import process
+ >>> from mailman.testing.helpers import get_queue_messages
>>> process(mlist, msg, dict(toowner=True))
- >>> len(virginq.files)
+ >>> messages = get_queue_messages('virgin')
+ >>> len(messages)
1
- >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
- >>> # Print only some of the meta data. The rest is uninteresting.
- >>> qdata['listname']
- u'[email protected]'
- >>> sorted(qdata['recips'])
- [u'[email protected]']
- >>> # Delete data that is time dependent or random
- >>> del qmsg['message-id']
- >>> del qmsg['date']
- >>> print qmsg.as_string()
+
+ >>> dump_msgdata(messages[0].msgdata)
+ _parsemsg : False
+ listname : [email protected]
+ nodecorate : True
+ recips : [u'[email protected]']
+ reduced_list_headers: True
+ version : 3
+
+ >>> print messages[0].msg.as_string()
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
@@ -58,19 +64,19 @@
To: [email protected]
X-Mailer: The Mailman Replybot
X-Ack: No
+ Message-ID: <...>
+ Date: ...
Precedence: bulk
<BLANKLINE>
admin autoresponse text
- >>> virginq.files
- []
Short circuiting
----------------
-Several headers in the original message determine whether an autoresponse
-should even be sent. For example, if the message has an "X-Ack: No" header,
-no auto-response is sent.
+Several headers in the original message determine whether an automatic
+response should even be sent. For example, if the message has an "X-Ack: No"
+header, no auto-response is sent.
>>> msg = message_from_string("""\
... From: [email protected]
@@ -78,24 +84,26 @@
...
... help me
... """)
+
>>> process(mlist, msg, dict(toowner=True))
- >>> virginq.files
+ >>> get_queue_messages('virgin')
[]
-Mailman itself can suppress autoresponses for certain types of internally
-crafted messages, by setting the 'noack' metadata key.
+Mailman itself can suppress automatic responses for certain types of
+internally crafted messages, by setting the 'noack' metadata key.
>>> msg = message_from_string("""\
... From: [email protected]
...
... help for you
... """)
+
>>> process(mlist, msg, dict(noack=True, toowner=True))
- >>> virginq.files
+ >>> get_queue_messages('virgin')
[]
If there is a Precedence: header with any of the values 'bulk', 'junk', or
-'list', then the autoresponse is also suppressed.
+'list', then the automatic response is also suppressed.
>>> msg = message_from_string("""\
... From: [email protected]
@@ -103,17 +111,19 @@
...
... hey!
... """)
+
>>> process(mlist, msg, dict(toowner=True))
- >>> virginq.files
+ >>> get_queue_messages('virgin')
[]
>>> msg.replace_header('precedence', 'junk')
>>> process(mlist, msg, dict(toowner=True))
- >>> virginq.files
+ >>> get_queue_messages('virgin')
[]
+
>>> msg.replace_header('precedence', 'list')
>>> process(mlist, msg, dict(toowner=True))
- >>> virginq.files
+ >>> get_queue_messages('virgin')
[]
Unless the X-Ack: header has a value of "yes", in which case, the Precedence
@@ -121,12 +131,19 @@
>>> msg['X-Ack'] = 'yes'
>>> process(mlist, msg, dict(toowner=True))
- >>> len(virginq.files)
+ >>> messages = get_queue_messages('virgin')
+ >>> len(messages)
1
- >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
- >>> del qmsg['message-id']
- >>> del qmsg['date']
- >>> print qmsg.as_string()
+
+ >>> dump_msgdata(messages[0].msgdata)
+ _parsemsg : False
+ listname : [email protected]
+ nodecorate : True
+ recips : [u'[email protected]']
+ reduced_list_headers: True
+ version : 3
+
+ >>> print messages[0].msg.as_string()
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
@@ -135,6 +152,8 @@
To: [email protected]
X-Mailer: The Mailman Replybot
X-Ack: No
+ Message-ID: <...>
+ Date: ...
Precedence: bulk
<BLANKLINE>
admin autoresponse text
@@ -149,19 +168,20 @@
>>> mlist.autorespond_requests = True
>>> mlist.autoresponse_request_text = u'robot autoresponse text'
+
>>> msg = message_from_string("""\
... From: [email protected]
... To: [email protected]
...
... help me
... """)
+
>>> process(mlist, msg, dict(torequest=True))
- >>> len(virginq.files)
+ >>> messages = get_queue_messages('virgin')
+ >>> len(messages)
1
- >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
- >>> del qmsg['message-id']
- >>> del qmsg['date']
- >>> print qmsg.as_string()
+
+ >>> print messages[0].msg.as_string()
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
@@ -170,6 +190,8 @@
To: [email protected]
X-Mailer: The Mailman Replybot
X-Ack: No
+ Message-ID: <...>
+ Date: ...
Precedence: bulk
<BLANKLINE>
robot autoresponse text
@@ -178,19 +200,20 @@
>>> mlist.autorespond_postings = True
>>> mlist.autoresponse_postings_text = u'postings autoresponse text'
+
>>> msg = message_from_string("""\
... From: [email protected]
... To: [email protected]
...
... help me
... """)
+
>>> process(mlist, msg, {})
- >>> len(virginq.files)
+ >>> messages = get_queue_messages('virgin')
+ >>> len(messages)
1
- >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
- >>> del qmsg['message-id']
- >>> del qmsg['date']
- >>> print qmsg.as_string()
+
+ >>> print messages[0].msg.as_string()
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
@@ -199,6 +222,8 @@
To: [email protected]
X-Mailer: The Mailman Replybot
X-Ack: No
+ Message-ID: <...>
+ Date: ...
Precedence: bulk
<BLANKLINE>
postings autoresponse text
@@ -207,8 +232,103 @@
Grace periods
-------------
-Auto-responses have a grace period, during which no additional responses will
-be sent. This is so as not to bombard the sender with responses. The grace
-period is measured in days.
-
-XXX Add grace period tests.
+Automatic responses have a grace period, during which no additional responses
+will be sent. This is so as not to bombard the sender with responses. The
+grace period is measured in days.
+
+ >>> mlist.autoresponse_graceperiod = datetime.timedelta(days=10)
+
+When a response is sent to a person via any of the owner, request, or postings
+addresses, the response date is recorded. The grace period is usually
+measured in days.
+
+ >>> msg = message_from_string("""\
+ ... From: [email protected]
+ ... To: [email protected]
+ ...
+ ... help
+ ... """)
+
+This is the first response to bperson, so it gets sent.
+
+ >>> process(mlist, msg, dict(toowner=True))
+ >>> print len(get_queue_messages('virgin'))
+ 1
+
+But with a grace period greater than zero, no subsequent response will be sent
+right now.
+
+ >>> process(mlist, msg, dict(toowner=True))
+ >>> print len(get_queue_messages('virgin'))
+ 0
+
+Fast forward 9 days and you still don't get a response.
+
+ >>> from mailman.utilities.datetime import factory
+ >>> factory.fast_forward(days=9)
+
+ >>> process(mlist, msg, dict(toowner=True))
+ >>> print len(get_queue_messages('virgin'))
+ 0
+
+But tomorrow, the sender will get a new auto-response.
+
+ >>> factory.fast_forward()
+ >>> process(mlist, msg, dict(toowner=True))
+ >>> print len(get_queue_messages('virgin'))
+ 1
+
+Of course, everything works the same way for messages to the request
+address, even if the sender is the same person...
+
+ >>> msg = message_from_string("""\
+ ... From: [email protected]
+ ... To: [email protected]
+ ...
+ ... help
+ ... """)
+
+ >>> process(mlist, msg, dict(torequest=True))
+ >>> print len(get_queue_messages('virgin'))
+ 1
+
+ >>> process(mlist, msg, dict(torequest=True))
+ >>> print len(get_queue_messages('virgin'))
+ 0
+
+ >>> factory.fast_forward(days=9)
+ >>> process(mlist, msg, dict(torequest=True))
+ >>> print len(get_queue_messages('virgin'))
+ 0
+
+ >>> factory.fast_forward()
+ >>> process(mlist, msg, dict(torequest=True))
+ >>> print len(get_queue_messages('virgin'))
+ 1
+
+...and for messages to the posting address.
+
+ >>> msg = message_from_string("""\
+ ... From: [email protected]
+ ... To: [email protected]
+ ...
+ ... help
+ ... """)
+
+ >>> process(mlist, msg, {})
+ >>> print len(get_queue_messages('virgin'))
+ 1
+
+ >>> process(mlist, msg, {})
+ >>> print len(get_queue_messages('virgin'))
+ 0
+
+ >>> factory.fast_forward(days=9)
+ >>> process(mlist, msg, {})
+ >>> print len(get_queue_messages('virgin'))
+ 0
+
+ >>> factory.fast_forward()
+ >>> process(mlist, msg, {})
+ >>> print len(get_queue_messages('virgin'))
+ 1
=== modified file 'src/mailman/pipeline/replybot.py'
--- src/mailman/pipeline/replybot.py 2009-02-10 03:19:18 +0000
+++ src/mailman/pipeline/replybot.py 2009-02-20 01:46:57 +0000
@@ -25,16 +25,18 @@
]
-import time
import logging
import datetime
from zope.interface import implements
from mailman import Utils
+from mailman.config import config
from mailman.email.message import Message, UserNotification
from mailman.i18n import _
+from mailman.interfaces.autorespond import IAutoResponseSet, Response
from mailman.interfaces.handler import IHandler
+from mailman.utilities.datetime import today
from mailman.utilities.string import expand
@@ -65,22 +67,25 @@
toadmin = msgdata.get('toowner')
torequest = msgdata.get('torequest')
if ((toadmin and not mlist.autorespond_admin) or
- (torequest and not mlist.autorespond_requests) or \
- (not toadmin and not torequest and not mlist.autorespond_postings)):
+ (torequest and not mlist.autorespond_requests) or \
+ (not toadmin and not torequest and not mlist.autorespond_postings)):
return
# Now see if we're in the grace period for this sender. graceperiod <= 0
# means always autorespond, as does an "X-Ack: yes" header (useful for
# debugging).
- now = time.time()
- graceperiod = mlist.autoresponse_graceperiod
- if graceperiod > NODELTA and ack <> 'yes':
+ response_set = IAutoResponseSet(mlist)
+ address = config.db.user_manager.get_address(msg.sender)
+ if address is None:
+ address = config.db.user_manager.create_address(msg.sender)
+ grace_period = mlist.autoresponse_graceperiod
+ if grace_period > NODELTA and ack <> 'yes':
if toadmin:
- quiet_until = mlist.admin_responses.get(msg.sender, 0)
+ last = response_set.last_response(address, Response.owner)
elif torequest:
- quiet_until = mlist.request_responses.get(msg.sender, 0)
+ last = response_set.last_response(address, Response.command)
else:
- quiet_until = mlist.postings_responses.get(msg.sender, 0)
- if quiet_until > now:
+ last = response_set.last_response(address, Response.postings)
+ if last is not None and last.date_sent + grace_period > today():
return
# Okay, we know we're going to auto-respond to this sender, craft the
# message, send it, and update the database.
@@ -108,15 +113,14 @@
outmsg['X-Ack'] = 'No'
outmsg.send(mlist)
# update the grace period database
- if graceperiod > NODELTA:
+ if grace_period > NODELTA:
# graceperiod is in days, we need # of seconds
- quiet_until = now + graceperiod * 24 * 60 * 60
if toadmin:
- mlist.admin_responses[msg.sender] = quiet_until
+ response_set.response_sent(address, Response.owner)
elif torequest:
- mlist.request_responses[msg.sender] = quiet_until
+ response_set.response_sent(address, Response.command)
else:
- mlist.postings_responses[msg.sender] = quiet_until
+ response_set.response_sent(address, Response.postings)
=== modified file 'src/mailman/utilities/datetime.py'
--- src/mailman/utilities/datetime.py 2009-02-19 05:18:35 +0000
+++ src/mailman/utilities/datetime.py 2009-02-20 01:46:57 +0000
@@ -44,7 +44,7 @@
predictable_today = None
def now(self, tz=None):
- return (yself.predictable_now
+ return (self.predictable_now
if self.testing_mode
else datetime.datetime.now(tz))
--
Primary development focus
https://code.launchpad.net/~mailman-coders/mailman/3.0
Your team Mailman Checkins is subscribed to branch lp:mailman.
To unsubscribe from this branch go to
https://code.launchpad.net/~mailman-coders/mailman/3.0/+edit-subscription.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe:
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org