1========================== 2Automatic response handler 3========================== 4 5Mailman has a autoreply handler that sends automatic responses to messages it 6receives on its posting address, owner address, or robot address. Automatic 7responses are subject to various conditions, such as headers in the original 8message or the amount of time since the last auto-response. 9 10 >>> mlist = create_list('_xtest@example.com') 11 >>> mlist.display_name = 'XTest' 12 13 14Basic automatic responding 15========================== 16 17Basic automatic responding occurs when the list is set up to respond to either 18its ``-owner`` address, its ``-request`` address, or to the posting address, 19and a message is sent to one of these addresses. A mailing list also has an 20automatic response grace period which specifies how much time must pass before 21a second response will be sent, with 0 meaning "there is no grace period". 22:: 23 24 >>> from datetime import timedelta 25 >>> from mailman.interfaces.autorespond import ResponseAction 26 27 >>> mlist.autorespond_owner = ResponseAction.respond_and_continue 28 >>> mlist.autoresponse_grace_period = timedelta() 29 >>> mlist.autoresponse_owner_text = 'owner autoresponse text' 30 31 >>> msg = message_from_string("""\ 32 ... From: aperson@example.com 33 ... To: _xtest-owner@example.com 34 ... 35 ... help 36 ... """) 37 38The preceding message to the mailing list's owner will trigger an automatic 39response. 40:: 41 42 >>> from mailman.testing.helpers import get_queue_messages 43 44 >>> handler = config.handlers['replybot'] 45 >>> handler.process(mlist, msg, dict(to_owner=True)) 46 >>> messages = get_queue_messages('virgin') 47 >>> len(messages) 48 1 49 50 >>> dump_msgdata(messages[0].msgdata) 51 _parsemsg : False 52 listid : _xtest.example.com 53 nodecorate : True 54 recipients : {'aperson@example.com'} 55 reduced_list_headers: True 56 version : 3 57 58 >>> print(messages[0].msg.as_string()) 59 MIME-Version: 1.0 60 Content-Type: text/plain; charset="us-ascii" 61 Content-Transfer-Encoding: 7bit 62 Subject: Auto-response for your message to the "XTest" mailing list 63 From: _xtest-bounces@example.com 64 To: aperson@example.com 65 X-Mailer: The Mailman Replybot 66 X-Ack: No 67 Message-ID: <...> 68 Date: ... 69 Precedence: bulk 70 <BLANKLINE> 71 owner autoresponse text 72 73 74Short circuiting 75================ 76 77Several headers in the original message determine whether an automatic 78response should even be sent. For example, if the message has an 79``X-Ack: No`` header, no auto-response is sent. 80:: 81 82 >>> msg = message_from_string("""\ 83 ... From: aperson@example.com 84 ... X-Ack: No 85 ... 86 ... help me 87 ... """) 88 89 >>> handler.process(mlist, msg, dict(to_owner=True)) 90 >>> get_queue_messages('virgin') 91 [] 92 93Mailman itself can suppress automatic responses for certain types of 94internally crafted messages, by setting the ``noack`` metadata key. 95:: 96 97 >>> msg = message_from_string("""\ 98 ... From: mailman@example.com 99 ... 100 ... help for you 101 ... """) 102 103 >>> handler.process(mlist, msg, dict(noack=True, to_owner=True)) 104 >>> get_queue_messages('virgin') 105 [] 106 107If there is a ``Precedence:`` header with any of the values ``bulk``, 108``junk``, or ``list``, then the automatic response is also suppressed. 109:: 110 111 >>> msg = message_from_string("""\ 112 ... From: asystem@example.com 113 ... Precedence: bulk 114 ... 115 ... hey! 116 ... """) 117 118 >>> handler.process(mlist, msg, dict(to_owner=True)) 119 >>> get_queue_messages('virgin') 120 [] 121 122 >>> msg.replace_header('precedence', 'junk') 123 >>> handler.process(mlist, msg, dict(to_owner=True)) 124 >>> get_queue_messages('virgin') 125 [] 126 127 >>> msg.replace_header('precedence', 'list') 128 >>> handler.process(mlist, msg, dict(to_owner=True)) 129 >>> get_queue_messages('virgin') 130 [] 131 132Unless the ``X-Ack:`` header has a value of ``yes``, in which case, the 133``Precedence`` header is ignored. 134:: 135 136 >>> msg['X-Ack'] = 'yes' 137 >>> handler.process(mlist, msg, dict(to_owner=True)) 138 >>> messages = get_queue_messages('virgin') 139 >>> len(messages) 140 1 141 142 >>> dump_msgdata(messages[0].msgdata) 143 _parsemsg : False 144 listid : _xtest.example.com 145 nodecorate : True 146 recipients : {'asystem@example.com'} 147 reduced_list_headers: True 148 version : 3 149 150 >>> print(messages[0].msg.as_string()) 151 MIME-Version: 1.0 152 Content-Type: text/plain; charset="us-ascii" 153 Content-Transfer-Encoding: 7bit 154 Subject: Auto-response for your message to the "XTest" mailing list 155 From: _xtest-bounces@example.com 156 To: asystem@example.com 157 X-Mailer: The Mailman Replybot 158 X-Ack: No 159 Message-ID: <...> 160 Date: ... 161 Precedence: bulk 162 <BLANKLINE> 163 owner autoresponse text 164 165 166Available auto-responses 167======================== 168 169As shown above, a message sent to the ``-owner`` address will get an 170auto-response with the text set for owner responses. Two other types of email 171will get auto-responses: those sent to the ``-request`` address... 172:: 173 174 >>> mlist.autorespond_requests = ResponseAction.respond_and_continue 175 >>> mlist.autoresponse_request_text = 'robot autoresponse text' 176 177 >>> msg = message_from_string("""\ 178 ... From: aperson@example.com 179 ... To: _xtest-request@example.com 180 ... 181 ... help me 182 ... """) 183 184 >>> handler.process(mlist, msg, dict(to_request=True)) 185 >>> messages = get_queue_messages('virgin') 186 >>> len(messages) 187 1 188 189 >>> print(messages[0].msg.as_string()) 190 MIME-Version: 1.0 191 Content-Type: text/plain; charset="us-ascii" 192 Content-Transfer-Encoding: 7bit 193 Subject: Auto-response for your message to the "XTest" mailing list 194 From: _xtest-bounces@example.com 195 To: aperson@example.com 196 X-Mailer: The Mailman Replybot 197 X-Ack: No 198 Message-ID: <...> 199 Date: ... 200 Precedence: bulk 201 <BLANKLINE> 202 robot autoresponse text 203 204...and those sent to the posting address. 205:: 206 207 >>> mlist.autorespond_postings = ResponseAction.respond_and_continue 208 >>> mlist.autoresponse_postings_text = 'postings autoresponse text' 209 210 >>> msg = message_from_string("""\ 211 ... From: aperson@example.com 212 ... To: _xtest@example.com 213 ... 214 ... help me 215 ... """) 216 217 >>> handler.process(mlist, msg, dict(to_list=True)) 218 >>> messages = get_queue_messages('virgin') 219 >>> len(messages) 220 1 221 222 >>> print(messages[0].msg.as_string()) 223 MIME-Version: 1.0 224 Content-Type: text/plain; charset="us-ascii" 225 Content-Transfer-Encoding: 7bit 226 Subject: Auto-response for your message to the "XTest" mailing list 227 From: _xtest-bounces@example.com 228 To: aperson@example.com 229 X-Mailer: The Mailman Replybot 230 X-Ack: No 231 Message-ID: <...> 232 Date: ... 233 Precedence: bulk 234 <BLANKLINE> 235 postings autoresponse text 236 237 238Grace periods 239============= 240 241Automatic responses have a grace period, during which no additional responses 242will be sent. This is so as not to bombard the sender with responses. The 243grace period is measured in days. 244 245 >>> mlist.autoresponse_grace_period = timedelta(days=10) 246 247When a response is sent to a person via any of the owner, request, or postings 248addresses, the response date is recorded. The grace period is usually 249measured in days. 250 251 >>> msg = message_from_string("""\ 252 ... From: bperson@example.com 253 ... To: _xtest-owner@example.com 254 ... 255 ... help 256 ... """) 257 258This is the first response to bperson, so it gets sent. 259 260 >>> handler.process(mlist, msg, dict(to_owner=True)) 261 >>> len(get_queue_messages('virgin')) 262 1 263 264But with a grace period greater than zero, no subsequent response will be sent 265right now. 266 267 >>> handler.process(mlist, msg, dict(to_owner=True)) 268 >>> len(get_queue_messages('virgin')) 269 0 270 271Fast forward 9 days and you still don't get a response. 272:: 273 274 >>> from mailman.utilities.datetime import factory 275 >>> factory.fast_forward(days=9) 276 277 >>> handler.process(mlist, msg, dict(to_owner=True)) 278 >>> len(get_queue_messages('virgin')) 279 0 280 281But tomorrow, the sender will get a new auto-response. 282 283 >>> factory.fast_forward() 284 >>> handler.process(mlist, msg, dict(to_owner=True)) 285 >>> len(get_queue_messages('virgin')) 286 1 287 288Of course, everything works the same way for messages to the request 289address, even if the sender is the same person... 290:: 291 292 >>> msg = message_from_string("""\ 293 ... From: bperson@example.com 294 ... To: _xtest-request@example.com 295 ... 296 ... help 297 ... """) 298 299 >>> handler.process(mlist, msg, dict(to_request=True)) 300 >>> len(get_queue_messages('virgin')) 301 1 302 303 >>> handler.process(mlist, msg, dict(to_request=True)) 304 >>> len(get_queue_messages('virgin')) 305 0 306 307 >>> factory.fast_forward(days=9) 308 >>> handler.process(mlist, msg, dict(to_request=True)) 309 >>> len(get_queue_messages('virgin')) 310 0 311 312 >>> factory.fast_forward() 313 >>> handler.process(mlist, msg, dict(to_request=True)) 314 >>> len(get_queue_messages('virgin')) 315 1 316 317...and for messages to the posting address. 318:: 319 320 >>> msg = message_from_string("""\ 321 ... From: bperson@example.com 322 ... To: _xtest@example.com 323 ... 324 ... help 325 ... """) 326 327 >>> handler.process(mlist, msg, dict(to_list=True)) 328 >>> len(get_queue_messages('virgin')) 329 1 330 331 >>> handler.process(mlist, msg, dict(to_list=True)) 332 >>> len(get_queue_messages('virgin')) 333 0 334 335 >>> factory.fast_forward(days=9) 336 >>> handler.process(mlist, msg, dict(to_list=True)) 337 >>> len(get_queue_messages('virgin')) 338 0 339 340 >>> factory.fast_forward() 341 >>> handler.process(mlist, msg, dict(to_list=True)) 342 >>> len(get_queue_messages('virgin')) 343 1 344