summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/core/initialize.py2
-rw-r--r--src/mailman/database/autorespond.py100
-rw-r--r--src/mailman/database/mailman.sql20
-rw-r--r--src/mailman/docs/autorespond.txt62
-rw-r--r--src/mailman/interfaces/autorespond.py87
5 files changed, 271 insertions, 0 deletions
diff --git a/src/mailman/core/initialize.py b/src/mailman/core/initialize.py
index bb16f0036..7edb963b7 100644
--- a/src/mailman/core/initialize.py
+++ b/src/mailman/core/initialize.py
@@ -116,6 +116,8 @@ def initialize_3():
"""
from mailman.app.registrar import adapt_domain_to_registrar
adapter_hooks.append(adapt_domain_to_registrar)
+ from mailman.database.autorespond import adapt_mailing_list_to_response_set
+ adapter_hooks.append(adapt_mailing_list_to_response_set)
diff --git a/src/mailman/database/autorespond.py b/src/mailman/database/autorespond.py
new file mode 100644
index 000000000..9561e804c
--- /dev/null
+++ b/src/mailman/database/autorespond.py
@@ -0,0 +1,100 @@
+# Copyright (C) 2009 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""Module stuff."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'AutoResponseRecord',
+ 'AutoResponseSet',
+ 'adapt_mailing_list_to_response_set',
+ ]
+
+
+from datetime import date
+from storm.locals import And, Date, Int, Reference
+from zope.interface import implements
+
+from mailman.config import config
+from mailman.database.model import Model
+from mailman.database.types import Enum
+from mailman.interfaces.autorespond import (
+ IAutoResponseRecord, IAutoResponseSet, Response)
+from mailman.interfaces.mailinglist import IMailingList
+
+
+
+class AutoResponseRecord(Model):
+ implements(IAutoResponseRecord)
+
+ id = Int(primary=True)
+
+ address_id = Int()
+ address = Reference(address_id, 'Address.id')
+
+ mailing_list_id = Int()
+ mailing_list = Reference(mailing_list_id, 'MailingList.id')
+
+ response_type = Enum()
+ date_sent = Date()
+
+ def __init__(self, mailing_list, address, response_type):
+ self.mailing_list = mailing_list
+ self.address = address
+ self.response_type = response_type
+ self.date_sent = date.today()
+
+
+
+class AutoResponseSet:
+ implements(IAutoResponseSet)
+
+ def __init__(self, mailing_list):
+ self._mailing_list = mailing_list
+
+ def todays_count(self, address, response_type):
+ """See `IAutoResponseSet`."""
+ return config.db.store.find(
+ AutoResponseRecord,
+ And(AutoResponseRecord.address == address,
+ AutoResponseRecord.mailing_list == self._mailing_list,
+ AutoResponseRecord.response_type == response_type,
+ AutoResponseRecord.date_sent == date.today())).count()
+
+ def response_sent(self, address, response_type):
+ """See `IAutoResponseSet`."""
+ response = AutoResponseRecord(
+ self._mailing_list, address, response_type)
+ config.db.store.add(response)
+
+
+
+def adapt_mailing_list_to_response_set(iface, obj):
+ """Adapt an `IMailingList` to an `IAutoResponseSet`.
+
+ :param iface: The interface to adapt to.
+ :type iface: `zope.interface.Interface`
+ :param obj: The object being adapted.
+ :type obj: `IMailingList`
+ :return: An `IAutoResponseSet` instance if adaptation succeeded or None if
+ it didn't.
+ """
+ return (AutoResponseSet(obj)
+ if IMailingList.providedBy(obj) and iface is IAutoResponseSet
+ else None)
diff --git a/src/mailman/database/mailman.sql b/src/mailman/database/mailman.sql
index 61d48285a..e2a239f1a 100644
--- a/src/mailman/database/mailman.sql
+++ b/src/mailman/database/mailman.sql
@@ -20,6 +20,26 @@ CREATE TABLE address (
CONSTRAINT address_user_id_fk FOREIGN KEY(user_id) REFERENCES user (id),
CONSTRAINT address_preferences_id_fk FOREIGN KEY(preferences_id) REFERENCES preferences (id)
);
+
+CREATE TABLE autoresponserecord (
+ id INTEGER NOT NULL,
+ address_id INTEGER,
+ mailing_list_id INTEGER,
+ response_type INTEGER,
+ date_sent TIMESTAMP,
+ PRIMARY KEY (id),
+ CONSTRAINT autoresponserecord_address_id_fk
+ FOREIGN KEY (address_id)
+ REFERENCES address (id),
+ CONSTRAINT autoresponserecord_mailing_list_id
+ FOREIGN KEY (mailing_list_id)
+ REFERENCES mailinglist (id)
+ );
+CREATE INDEX ix_autoresponserecord_address_id
+ ON autoresponserecord (address_id);
+CREATE INDEX ix_autoresponserecord_mailing_list_id
+ ON autoresponserecord (mailing_list_id);
+
CREATE TABLE language (
id INTEGER NOT NULL,
code TEXT,
diff --git a/src/mailman/docs/autorespond.txt b/src/mailman/docs/autorespond.txt
new file mode 100644
index 000000000..29af740ec
--- /dev/null
+++ b/src/mailman/docs/autorespond.txt
@@ -0,0 +1,62 @@
+Automatic responder
+===================
+
+In various situations, Mailman will send an automatic response to the author
+of an email message. For example, if someone sends a command to the -request
+address, Mailman will send a response, but to cut down on third party spam,
+the sender will only get a certain number of responses per day.
+
+First, given a mailing list you need to adapt it to an IAutoResponseSet.
+
+ >>> mlist = create_list('test@example.com')
+ >>> from mailman.interfaces.autorespond import IAutoResponseSet
+ >>> response_set = IAutoResponseSet(mlist)
+
+ >>> from zope.interface.verify import verifyObject
+ >>> verifyObject(IAutoResponseSet, response_set)
+ True
+
+You can't adapt other objects to an IAutoResponseSet.
+
+ >>> IAutoResponseSet(object())
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', ...
+
+There are various kinds of response types. For example, Mailman will send an
+automatic response when messages are held for approval, or when it receives an
+email command. You can find out how many responses for a particular address
+have already been sent today.
+
+ >>> address = config.db.user_manager.create_address(
+ ... u'aperson@example.com')
+ >>> from mailman.interfaces.autorespond import Response
+ >>> response_set.todays_count(address, Response.hold)
+ 0
+ >>> response_set.todays_count(address, Response.command)
+ 0
+
+Using the response set, we can record that a hold response is sent to the
+address.
+
+ >>> response_set.response_sent(address, Response.hold)
+ >>> response_set.todays_count(address, Response.hold)
+ 1
+ >>> response_set.todays_count(address, Response.command)
+ 0
+
+We can also record that a command response was sent.
+
+ >>> response_set.response_sent(address, Response.command)
+ >>> response_set.todays_count(address, Response.hold)
+ 1
+ >>> response_set.todays_count(address, Response.command)
+ 1
+
+Let's send one more.
+
+ >>> response_set.response_sent(address, Response.command)
+ >>> response_set.todays_count(address, Response.hold)
+ 1
+ >>> response_set.todays_count(address, Response.command)
+ 2
diff --git a/src/mailman/interfaces/autorespond.py b/src/mailman/interfaces/autorespond.py
new file mode 100644
index 000000000..fa4d19de6
--- /dev/null
+++ b/src/mailman/interfaces/autorespond.py
@@ -0,0 +1,87 @@
+# Copyright (C) 2009 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""Autoresponder."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IAutoResponseRecord',
+ 'IAutoResponseSet',
+ 'Response',
+ ]
+
+
+from munepy import Enum
+from zope.interface import Interface, Attribute
+
+
+
+class Response(Enum):
+ # Your message was held for approval.
+ hold = 1
+ # Email commands, i.e. -request messages.
+ command = 2
+
+
+
+class IAutoResponseRecord(Interface):
+ """An auto-response record.
+
+ Every time Mailman sends an automatic response to an address, on a
+ specific mailing list for a specific purpose, it records the response. To
+ limit the effects of blow back and other third party spam, Mailman will
+ only send a certain number of such automatic response per day. After the
+ maximum is reached, it will not send another such response to the same
+ address until the next day.
+ """
+ address = Attribute("""The email address being sent the auto-response.""")
+
+ mailing_list = Attribute(
+ """The mailing list sending the auto-response.""")
+
+ response_type = Attribute("""The type of response sent.""")
+
+
+
+class IAutoResponseSet(Interface):
+ """Matching and setting auto-responses.
+
+ The `IAutoResponseSet` is contexted to a particular mailing list.
+ """
+
+ def todays_count(address, response_type):
+ """The number of responses sent to an address today.
+
+ :param address: The address who is the recipient of the auto-response.
+ :type address: `IAddress`
+ :param response_type: The response type being sent.
+ :type response_type: `Response`
+ :return: The number of auto-responses already received by the user
+ today, of this type, from this mailing list.
+ :rtype: int
+ """
+
+ def response_sent(address, response_type):
+ """Record the fact that another response is being sent to the address.
+
+ :param address: The address who is the recipient of the auto-response.
+ :type address: `IAddress`
+ :param response_type: The response type being sent.
+ :type response_type: `Response`
+ """