[tor-commits] [bridgedb/develop] Make our email autoresponder more usable.

phw at torproject.org phw at torproject.org
Mon Apr 6 22:42:56 UTC 2020


commit 1958bbb7e95df0f810e219955595f353933e1302
Author: Philipp Winter <phw at nymity.ch>
Date:   Tue Mar 31 09:37:37 2020 -0700

    Make our email autoresponder more usable.
    
    So far, our autoresponder would only send you bridges if your request
    was valid.  Unfortunately, it's not very easy to figure out what a
    correct request looks like.  This patch returns bridges regardless of if
    the request was valid or not.  We also remove the "help" autoresponse
    because there's no longer a need for it, and we simplify BridgeDB's auto
    response, hopefully making it less frustrating for users.
    
    This fixes <https://bugs.torproject.org/30941>.
---
 CHANGELOG                                    |   6 ++
 bridgedb/distributors/email/autoresponder.py |  10 --
 bridgedb/distributors/email/distributor.py   |   6 +-
 bridgedb/distributors/email/request.py       |  20 ++--
 bridgedb/distributors/email/templates.py     |  83 +++++-----------
 bridgedb/i18n/templates/bridgedb.pot         | 143 +++++++++++----------------
 bridgedb/metrics.py                          |   7 +-
 bridgedb/strings.py                          |  49 ++++-----
 bridgedb/test/test_email_autoresponder.py    |   5 +-
 bridgedb/test/test_email_request.py          |  46 ++++-----
 bridgedb/test/test_email_templates.py        |  31 +-----
 11 files changed, 151 insertions(+), 255 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 3e179e1..f85283b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+        * FIXES https://bugs.torproject.org/30941
+        Make our email responder more usable.  This patch removes the concept of
+        "valid" email commands and returns bridges (obfs4, for now) no matter
+        what the user sends.  BridgeDB still supports email commands in case the
+        user needs a vanilla or IPv6 bridge.
+
         * FIXES https://bugs.torproject.org/29686
         Rename files that contain "Bridges" to "bridgerings", to eliminate
         headache on file systems that are case insensitive.
diff --git a/bridgedb/distributors/email/autoresponder.py b/bridgedb/distributors/email/autoresponder.py
index 3711eae..5e1fcd0 100644
--- a/bridgedb/distributors/email/autoresponder.py
+++ b/bridgedb/distributors/email/autoresponder.py
@@ -55,7 +55,6 @@ from bridgedb import safelog
 from bridgedb.distributors.email import dkim
 from bridgedb.distributors.email import request
 from bridgedb.distributors.email import templates
-from bridgedb.distributors.email.distributor import EmailRequestedHelp
 from bridgedb.distributors.email.distributor import EmailRequestedKey
 from bridgedb.distributors.email.distributor import TooSoonEmail
 from bridgedb.distributors.email.distributor import IgnoreEmail
@@ -98,18 +97,9 @@ def createResponseBody(lines, context, client, lang='en'):
         bridgeRequest = request.determineBridgeRequestOptions(lines)
         bridgeRequest.client = str(client)
 
-        # The request was invalid, respond with a help email which explains
-        # valid email commands:
-        if not bridgeRequest.isValid():
-            raise EmailRequestedHelp("Email request from '%s' was invalid."
-                                     % str(client))
-
         # Otherwise they must have requested bridges:
         interval = context.schedule.intervalStart(time.time())
         bridges = context.distributor.getBridges(bridgeRequest, interval)
-    except EmailRequestedHelp as error:
-        logging.info(error)
-        return templates.buildWelcomeText(translator, client)
     except EmailRequestedKey as error:
         logging.info(error)
         return templates.buildKeyMessage(translator, client)
diff --git a/bridgedb/distributors/email/distributor.py b/bridgedb/distributors/email/distributor.py
index a90e725..c6ac2fd 100644
--- a/bridgedb/distributors/email/distributor.py
+++ b/bridgedb/distributors/email/distributor.py
@@ -20,7 +20,7 @@ bridgedb.distributors.email.autoresponder
 A :class:`~bridgedb.distribute.Distributor` which hands out :class:`bridges
 <bridgedb.bridges.Bridge>` to clients via an email interface.
 
-.. inheritance-diagram:: IgnoreEmail TooSoonEmail EmailRequestedHelp EmailRequestedKey EmailDistributor
+.. inheritance-diagram:: IgnoreEmail TooSoonEmail EmailRequestedKey EmailDistributor
     :parts: 1
 """
 
@@ -55,10 +55,6 @@ class TooSoonEmail(addr.BadEmail):
     """Raised when we got a request from this address too recently."""
 
 
-class EmailRequestedHelp(Exception):
-    """Raised when a client has emailed requesting help."""
-
-
 class EmailRequestedKey(Exception):
     """Raised when an incoming email requested a copy of our GnuPG keys."""
 
diff --git a/bridgedb/distributors/email/request.py b/bridgedb/distributors/email/request.py
index 83c203d..90576c1 100644
--- a/bridgedb/distributors/email/request.py
+++ b/bridgedb/distributors/email/request.py
@@ -43,8 +43,8 @@ from __future__ import unicode_literals
 import logging
 import re
 
+from bridgedb import strings
 from bridgedb import bridgerequest
-from bridgedb.distributors.email.distributor import EmailRequestedHelp
 from bridgedb.distributors.email.distributor import EmailRequestedKey
 
 
@@ -61,7 +61,6 @@ UNBLOCKED_PATTERN = re.compile(UNBLOCKED_REGEXP)
 #: Regular expressions that we use to match for email commands.  Any command is
 #: valid as long as it wasn't quoted, i.e., the line didn't start with a '>'
 #: character.
-HELP_LINE      = re.compile("([^>].*)?h[ae]lp")
 GET_LINE       = re.compile("([^>].*)?get")
 KEY_LINE       = re.compile("([^>].*)?key")
 IPV6_LINE      = re.compile("([^>].*)?ipv6")
@@ -69,14 +68,13 @@ TRANSPORT_LINE = re.compile("([^>].*)?transport")
 UNBLOCKED_LINE = re.compile("([^>].*)?unblocked")
 
 def determineBridgeRequestOptions(lines):
-    """Figure out which :mod:`~bridgedb.filters` to apply, or offer help.
+    """Figure out which :mod:`~bridgedb.filters` to apply.
 
     .. note:: If any ``'transport TYPE'`` was requested, or bridges not
         blocked in a specific CC (``'unblocked CC'``), then the ``TYPE``
         and/or ``CC`` will *always* be stored as a *lowercase* string.
 
     :param list lines: A list of lines from an email, including the headers.
-    :raises EmailRequestedHelp: if the client requested help.
     :raises EmailRequestedKey: if the client requested our GnuPG key.
     :rtype: :class:`EmailBridgeRequest`
     :returns: A :class:`~bridgerequest.BridgeRequest` with all of the requested
@@ -92,9 +90,6 @@ def determineBridgeRequestOptions(lines):
         if not line: skippedHeaders = True
         if not skippedHeaders: continue
 
-        if HELP_LINE.match(line) is not None:
-            raise EmailRequestedHelp("Client requested help.")
-
         if GET_LINE.match(line) is not None:
             request.isValid(True)
             logging.debug("Email request was valid.")
@@ -108,6 +103,17 @@ def determineBridgeRequestOptions(lines):
         if UNBLOCKED_LINE.match(line) is not None:
             request.withoutBlockInCountry(line)
 
+    # We cannot expect all users to understand BridgeDB's commands, so we will
+    # return bridges even if the request was invalid.
+    if not request.isValid():
+        logging.debug("Email request was invalid.")
+        request.isValid(True)
+        # We will respond with our default transport protocol.
+        if not len(request.transports):
+            # Note that this variable must satisfy TRANSPORT_PATTERN.
+            default_transport = "transport %s" % strings._getDefaultTransport()
+            request.withPluggableTransportType(default_transport)
+
     logging.debug("Generating hashring filters for request.")
     request.generateFilters()
     return request
diff --git a/bridgedb/distributors/email/templates.py b/bridgedb/distributors/email/templates.py
index d618c37..2eb0a31 100644
--- a/bridgedb/distributors/email/templates.py
+++ b/bridgedb/distributors/email/templates.py
@@ -21,9 +21,6 @@ bridgedb.distributors.email.templates
 Templates for formatting emails sent out by the email distributor.
 """
 
-from __future__ import print_function
-from __future__ import unicode_literals
-
 import logging
 import os
 
@@ -34,12 +31,17 @@ from bridgedb.distributors.email.distributor import MAX_EMAIL_RATE
 
 
 def addCommands(template):
-    """Add some text telling a client about supported email command, as well as
-    which Pluggable Transports are currently available.
+    """Add text telling a client about supported email command.
+
+    :type template: ``gettext.NullTranslation`` or ``gettext.GNUTranslation``
+    :param template: A gettext translations instance, optionally with fallback
+        languages set.
+    :rtype: str
+    :returns: A string explaining email commands.
     """
     # Tell them about the various email commands:
     cmdlist = []
-    cmdlist.append(template.gettext(strings.EMAIL_MISC_TEXT.get(3)))
+    cmdlist.append(template.gettext(strings.EMAIL_MISC_TEXT.get(3)) + "\n")
     for cmd, desc in strings.EMAIL_COMMANDS.items():
         command  = '  '
         command += cmd
@@ -48,29 +50,20 @@ def addCommands(template):
         command += template.gettext(desc)
         cmdlist.append(command)
 
-    commands  = "\n".join(cmdlist) + "\n\n"
-    # And include the currently supported transports:
-    commands += template.gettext(strings.EMAIL_MISC_TEXT.get(5))
-    commands += "\n"
-    for pt in strings._getSupportedTransports():
-        commands += '  ' + pt + "\n"
+    return "\n".join(cmdlist) + "\n\n"
 
-    return commands
+def addGreeting(template):
+    """Our "greeting" clarifies that this is an automated email response.
 
-def addGreeting(template, clientName=None, welcome=False):
-    greeting = ""
-    clientName = clientName.decode('utf-8') if isinstance(clientName, bytes) else clientName
-
-    if not clientName:
-        greeting = template.gettext(strings.EMAIL_MISC_TEXT[7])
-    else:
-        greeting = template.gettext(strings.EMAIL_MISC_TEXT[6]) % clientName
+    :type template: ``gettext.NullTranslation`` or ``gettext.GNUTranslation``
+    :param template: A gettext translations instance, optionally with fallback
+        languages set.
+    :rtype: str
+    :returns: A string containing our "greeting".
+    """
 
-    if greeting:
-        if welcome:
-            greeting += u' '
-            greeting += template.gettext(strings.EMAIL_MISC_TEXT[4])
-        greeting += u'\n\n'
+    greeting = template.gettext(strings.EMAIL_MISC_TEXT[0])
+    greeting += u"\n\n"
 
     return greeting
 
@@ -79,11 +72,10 @@ def addKeyfile(template):
 
 def addBridgeAnswer(template, answer):
     # Give the user their bridges, i.e. the `answer`:
-    bridgeLines  = template.gettext(strings.EMAIL_MISC_TEXT[0])
-    bridgeLines += u"\n\n"
+    bridgeLines = u""
     bridgeLines += template.gettext(strings.EMAIL_MISC_TEXT[1])
     bridgeLines += u"\n\n"
-    bridgeLines += u"%s\n\n" % answer
+    bridgeLines += u"%s\n" % answer
 
     return bridgeLines
 
@@ -94,40 +86,15 @@ def addHowto(template):
     :param template: A gettext translations instance, optionally with fallback
         languages set.
     """
-    howToTBB  = template.gettext(strings.HOWTO_TBB[1]) % strings.EMAIL_SPRINTF["HOWTO_TBB1"]
-    howToTBB += u'\n\n'
-    howToTBB += strings.EMAIL_REFERENCE_LINKS.get("HOWTO_TBB1")
-    howToTBB += strings.EMAIL_REFERENCE_LINKS.get("HOWTO_TBB2")
-    howToTBB += strings.EMAIL_REFERENCE_LINKS.get("HOWTO_TBB3")
-    howToTBB += u'\n\n'
-    return howToTBB
+    return template.gettext(strings.HOWTO_TBB[2])
 
 def buildKeyMessage(template, clientAddress=None):
     message  = addKeyfile(template)
     return message
 
-def buildWelcomeText(template, clientAddress=None):
-    sections = []
-    sections.append(addGreeting(template, clientAddress.local, welcome=True))
-
-    commands = addCommands(template)
-    sections.append(commands)
-
-    # Include the same messages as the homepage of the HTTPS distributor:
-    welcome  = template.gettext(strings.WELCOME[0]) % strings.EMAIL_SPRINTF["WELCOME0"]
-    welcome += template.gettext(strings.WELCOME[1])
-    welcome += template.gettext(strings.WELCOME[2]) % strings.EMAIL_SPRINTF["WELCOME2"]
-    sections.append(welcome)
-
-    message  = u"\n\n".join(sections)
-    # Add the markdown links at the end:
-    message += strings.EMAIL_REFERENCE_LINKS.get("WELCOME0")
-
-    return message
-
 def buildAnswerMessage(template, clientAddress=None, answer=None):
     try:
-        message  = addGreeting(template, clientAddress.local)
+        message  = addGreeting(template)
         message += addBridgeAnswer(template, answer)
         message += addHowto(template)
         message += u'\n\n'
@@ -139,11 +106,9 @@ def buildAnswerMessage(template, clientAddress=None, answer=None):
     return message
 
 def buildSpamWarning(template, clientAddress=None):
-    message = addGreeting(template, clientAddress.local)
+    message = addGreeting(template)
 
     try:
-        message += template.gettext(strings.EMAIL_MISC_TEXT[0])
-        message += u"\n\n"
         message += template.gettext(strings.EMAIL_MISC_TEXT[2]) \
                    % str(MAX_EMAIL_RATE / 3600)
     except Exception as error:  # pragma: no cover
diff --git a/bridgedb/i18n/templates/bridgedb.pot b/bridgedb/i18n/templates/bridgedb.pot
index 8fa69c6..65f8ca6 100644
--- a/bridgedb/i18n/templates/bridgedb.pot
+++ b/bridgedb/i18n/templates/bridgedb.pot
@@ -5,11 +5,11 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: bridgedb 0.9.4+8.g5afd164.dirty\n"
+"Project-Id-Version: bridgedb 0.10.0+8.g97621c0\n"
 "Report-Msgid-Bugs-To: "
 "'https://trac.torproject.org/projects/tor/newticket?component=BridgeDB&keywords"
 "=bridgedb-reported,msgid&cc=isis,sysrqb&owner=isis'\n"
-"POT-Creation-Date: 2020-03-24 10:22-0700\n"
+"POT-Creation-Date: 2020-04-06 13:53-0700\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -66,6 +66,10 @@ msgstr ""
 msgid "Changelog"
 msgstr ""
 
+#: bridgedb/distributors/https/templates/base.html:102
+msgid "Public Keys"
+msgstr ""
+
 #: bridgedb/distributors/https/templates/bridges.html:35
 msgid "Select All"
 msgstr ""
@@ -83,7 +87,7 @@ msgstr ""
 #. for Italian, you might translate this into "Mama mia!",
 #. or for French: "Sacrebleu!". :)
 #: bridgedb/distributors/https/templates/bridges.html:67
-#: bridgedb/distributors/https/templates/bridges.html:125
+#: bridgedb/distributors/https/templates/bridges.html:119
 msgid "Uh oh, spaghettios!"
 msgstr ""
 
@@ -97,12 +101,12 @@ msgid ""
 "your bridge lines onto mobile and other devices."
 msgstr ""
 
-#: bridgedb/distributors/https/templates/bridges.html:131
+#: bridgedb/distributors/https/templates/bridges.html:125
 msgid "There currently aren't any bridges available..."
 msgstr ""
 
-#: bridgedb/distributors/https/templates/bridges.html:133
-#: bridgedb/distributors/https/templates/bridges.html:137
+#: bridgedb/distributors/https/templates/bridges.html:127
+#: bridgedb/distributors/https/templates/bridges.html:131
 #, python-format
 msgid " Perhaps you should try %s going back %s and choosing a different bridge type!"
 msgstr ""
@@ -176,15 +180,15 @@ msgstr ""
 msgid "%sG%set Bridges"
 msgstr ""
 
-#: bridgedb/strings.py:43
-msgid "[This is an automated message; please do not reply.]"
+#: bridgedb/strings.py:42
+msgid "[This is an automated email.]"
 msgstr ""
 
-#: bridgedb/strings.py:45
+#: bridgedb/strings.py:44
 msgid "Here are your bridges:"
 msgstr ""
 
-#: bridgedb/strings.py:47
+#: bridgedb/strings.py:46
 #, python-format
 msgid ""
 "You have exceeded the rate limit. Please slow down! The minimum time between\n"
@@ -192,48 +196,17 @@ msgid ""
 "ignored."
 msgstr ""
 
-#: bridgedb/strings.py:50
-msgid "COMMANDs: (combine COMMANDs to specify multiple options simultaneously)"
-msgstr ""
-
-#. TRANSLATORS: Please DO NOT translate the word "BridgeDB".
-#: bridgedb/strings.py:53
-msgid "Welcome to BridgeDB!"
-msgstr ""
-
-#. TRANSLATORS: Please DO NOT translate the words "transport" or "TYPE".
-#: bridgedb/strings.py:55
-msgid "Currently supported transport TYPEs:"
-msgstr ""
-
-#: bridgedb/strings.py:56
-#, python-format
-msgid "Hey, %s!"
-msgstr ""
-
-#: bridgedb/strings.py:57
-msgid "Hello, friend!"
-msgstr ""
-
-#: bridgedb/distributors/https/templates/base.html:102 bridgedb/strings.py:58
-msgid "Public Keys"
-msgstr ""
-
-#. TRANSLATORS: This string will end up saying something like:
-#. "This email was generated with rainbows, unicorns, and sparkles
-#. for alice at example.com on Friday, 09 May, 2014 at 18:59:39."
-#: bridgedb/strings.py:62
-#, python-format
+#: bridgedb/strings.py:49
 msgid ""
-"This email was generated with rainbows, unicorns, and sparkles\n"
-"for %s on %s at %s."
+"If these bridges are not what you need, reply to this email with one of\n"
+"the following commands in the message body:"
 msgstr ""
 
 #. TRANSLATORS: Please DO NOT translate "BridgeDB".
 #. TRANSLATORS: Please DO NOT translate "Pluggable Transports".
 #. TRANSLATORS: Please DO NOT translate "Tor".
 #. TRANSLATORS: Please DO NOT translate "Tor Network".
-#: bridgedb/strings.py:72
+#: bridgedb/strings.py:59
 #, python-format
 msgid ""
 "BridgeDB can provide bridges with several %stypes of Pluggable Transports%s,\n"
@@ -245,7 +218,7 @@ msgid ""
 msgstr ""
 
 #. TRANSLATORS: Please DO NOT translate "Pluggable Transports".
-#: bridgedb/strings.py:79
+#: bridgedb/strings.py:66
 msgid ""
 "Some bridges with IPv6 addresses are also available, though some Pluggable\n"
 "Transports aren't IPv6 compatible.\n"
@@ -257,7 +230,7 @@ msgstr ""
 #. regular, or unexciting". Like vanilla ice cream. It refers to bridges
 #. which do not have Pluggable Transports, and only speak the regular,
 #. boring Tor protocol. Translate it as you see fit. Have fun with it.
-#: bridgedb/strings.py:88
+#: bridgedb/strings.py:75
 #, python-format
 msgid ""
 "Additionally, BridgeDB has plenty of plain-ol'-vanilla bridges %s without any"
@@ -268,21 +241,21 @@ msgid ""
 "\n"
 msgstr ""
 
-#: bridgedb/strings.py:101 bridgedb/test/test_https.py:356
+#: bridgedb/strings.py:87 bridgedb/test/test_https.py:356
 msgid "What are bridges?"
 msgstr ""
 
-#: bridgedb/strings.py:102
+#: bridgedb/strings.py:88
 #, python-format
 msgid "%s Bridges %s are Tor relays that help you circumvent censorship."
 msgstr ""
 
-#: bridgedb/strings.py:107
+#: bridgedb/strings.py:93
 msgid "I need an alternative way of getting bridges!"
 msgstr ""
 
 #. TRANSLATORS: Please DO NOT translate "get transport obfs4".
-#: bridgedb/strings.py:109
+#: bridgedb/strings.py:95
 #, python-format
 msgid ""
 "Another way to get bridges is to send an email to %s. Leave the email subject"
@@ -294,32 +267,32 @@ msgid ""
 "providers: %s or %s."
 msgstr ""
 
-#: bridgedb/strings.py:117
+#: bridgedb/strings.py:103
 msgid "My bridges don't work! I need help!"
 msgstr ""
 
 #. TRANSLATORS: Please DO NOT translate "Tor Browser".
 #. TRANSLATORS: The two '%s' are substituted with "Tor Browser Manual" and
 #. "Support Portal", respectively.
-#: bridgedb/strings.py:121
+#: bridgedb/strings.py:107
 #, python-format
 msgid "If your Tor Browser cannot connect, please take a look at the %s and our %s."
 msgstr ""
 
-#: bridgedb/strings.py:125
+#: bridgedb/strings.py:111
 msgid "Here are your bridge lines:"
 msgstr ""
 
-#: bridgedb/strings.py:126
+#: bridgedb/strings.py:112
 msgid "Get Bridges!"
 msgstr ""
 
-#: bridgedb/strings.py:130
+#: bridgedb/strings.py:116
 msgid "Bridge distribution mechanisms"
 msgstr ""
 
 #. TRANSLATORS: Please DO NOT translate "BridgeDB", "HTTPS", and "Moat".
-#: bridgedb/strings.py:132
+#: bridgedb/strings.py:118
 #, python-format
 msgid ""
 "BridgeDB implements four mechanisms to distribute bridges: \"HTTPS\", "
@@ -333,7 +306,7 @@ msgid ""
 "mechanisms is."
 msgstr ""
 
-#: bridgedb/strings.py:138
+#: bridgedb/strings.py:124
 #, python-format
 msgid ""
 "The \"HTTPS\" distribution mechanism hands out bridges over this website.  To"
@@ -343,7 +316,7 @@ msgid ""
 "solve the subsequent CAPTCHA."
 msgstr ""
 
-#: bridgedb/strings.py:142
+#: bridgedb/strings.py:128
 #, python-format
 msgid ""
 "The \"Moat\" distribution mechanism is part of Tor Browser, allowing users to"
@@ -356,7 +329,7 @@ msgid ""
 "bridges."
 msgstr ""
 
-#: bridgedb/strings.py:148
+#: bridgedb/strings.py:134
 #, python-format
 msgid ""
 "Users can request bridges from the \"Email\" distribution mechanism by "
@@ -366,11 +339,11 @@ msgid ""
 "email body."
 msgstr ""
 
-#: bridgedb/strings.py:152
+#: bridgedb/strings.py:138
 msgid "Reserved"
 msgstr ""
 
-#: bridgedb/strings.py:153
+#: bridgedb/strings.py:139
 #, python-format
 msgid ""
 "BridgeDB maintains a small number of bridges that are not distributed\n"
@@ -384,11 +357,11 @@ msgid ""
 "called \"Unallocated\" in %sbridge pool assignment%s files."
 msgstr ""
 
-#: bridgedb/strings.py:160
+#: bridgedb/strings.py:146
 msgid "None"
 msgstr ""
 
-#: bridgedb/strings.py:161
+#: bridgedb/strings.py:147
 msgid ""
 "Bridges whose distribution mechanism is \"None\" are not distributed by "
 "BridgeDB.\n"
@@ -399,33 +372,33 @@ msgid ""
 "it will then change to the bridge's actual distribution mechanism.\n"
 msgstr ""
 
-#: bridgedb/strings.py:171
+#: bridgedb/strings.py:157
 msgid "Please select options for bridge type:"
 msgstr ""
 
-#: bridgedb/strings.py:172
+#: bridgedb/strings.py:158
 msgid "Do you need IPv6 addresses?"
 msgstr ""
 
-#: bridgedb/strings.py:173
+#: bridgedb/strings.py:159
 #, python-format
 msgid "Do you need a %s?"
 msgstr ""
 
-#: bridgedb/strings.py:177
+#: bridgedb/strings.py:163
 msgid "Your browser is not displaying images properly."
 msgstr ""
 
-#: bridgedb/strings.py:178
+#: bridgedb/strings.py:164
 msgid "Enter the characters from the image above..."
 msgstr ""
 
-#: bridgedb/strings.py:182
+#: bridgedb/strings.py:168
 msgid "How to start using your bridges"
 msgstr ""
 
 #. TRANSLATORS: Please DO NOT translate "Tor Browser".
-#: bridgedb/strings.py:184
+#: bridgedb/strings.py:170
 #, python-format
 msgid ""
 " First, you need to %sdownload Tor Browser%s. Our Tor Browser User\n"
@@ -434,29 +407,29 @@ msgid ""
 " are using Android, %sclick here%s."
 msgstr ""
 
-#: bridgedb/strings.py:192
-msgid "Displays this message."
+#: bridgedb/strings.py:175
+msgid ""
+"Add these bridges to your Tor Browser by opening your browser\n"
+"preferences, clicking on \"Tor\", and then adding them to the \"Provide a\n"
+"bridge\" field."
 msgstr ""
 
-#. TRANSLATORS: Please try to make it clear that "vanilla" here refers to the
-#. same non-Pluggable Transport bridges described above as being
-#. "plain-ol'-vanilla" bridges.
-#: bridgedb/strings.py:196
-msgid "Request vanilla bridges."
+#: bridgedb/strings.py:182
+msgid "(Request unobfuscated Tor bridges.)"
 msgstr ""
 
-#: bridgedb/strings.py:197
-msgid "Request IPv6 bridges."
+#: bridgedb/strings.py:183
+msgid "(Request IPv6 bridges.)"
 msgstr ""
 
-#. TRANSLATORS: Please DO NOT translate the word the word "TYPE".
-#: bridgedb/strings.py:199
-msgid "Request a Pluggable Transport by TYPE."
+#. TRANSLATORS: Please DO NOT translate the word "TYPE".
+#: bridgedb/strings.py:185
+msgid "(Request obfuscated bridges. Replace TYPE with 'obfs4'.)"
 msgstr ""
 
 #. TRANSLATORS: Please DO NOT translate "BridgeDB".
 #. TRANSLATORS: Please DO NOT translate "GnuPG".
-#: bridgedb/strings.py:202
-msgid "Get a copy of BridgeDB's public GnuPG key."
+#: bridgedb/strings.py:189
+msgid "(Get a copy of BridgeDB's public GnuPG key.)"
 msgstr ""
 
diff --git a/bridgedb/metrics.py b/bridgedb/metrics.py
index bb888d9..48713f0 100644
--- a/bridgedb/metrics.py
+++ b/bridgedb/metrics.py
@@ -22,7 +22,6 @@ import datetime
 from bridgedb import geo
 from bridgedb.distributors.common.http import getClientIP
 from bridgedb.distributors.email import request
-from bridgedb.distributors.email.distributor import EmailRequestedHelp
 
 from twisted.mail.smtp import Address
 
@@ -387,11 +386,7 @@ class EmailMetrics(Metrics):
             emailAddr = emailAddrs[0]
 
         # Get the requested transport protocol.
-        try:
-            br = request.determineBridgeRequestOptions(
-                    smtpAutoresp.incoming.lines)
-        except EmailRequestedHelp:
-            return
+        br = request.determineBridgeRequestOptions( smtpAutoresp.incoming.lines)
         bridgeType = "vanilla" if not len(br.transports) else br.transports[0]
 
         # Over email, transports are requested by typing them.  Typos happen
diff --git a/bridgedb/strings.py b/bridgedb/strings.py
index b6a7f1f..f3ea849 100644
--- a/bridgedb/strings.py
+++ b/bridgedb/strings.py
@@ -38,30 +38,17 @@ def _(text):
     return text
 
 
-# TRANSLATORS: Please do not translate the word "TYPE".
 EMAIL_MISC_TEXT = {
     0: _("""\
-[This is an automated message; please do not reply.]"""),
+[This is an automated email.]"""),
     1: _("""\
 Here are your bridges:"""),
     2: _("""\
 You have exceeded the rate limit. Please slow down! The minimum time between
 emails is %s hours. All further emails during this time period will be ignored."""),
     3: _("""\
-COMMANDs: (combine COMMANDs to specify multiple options simultaneously)"""),
-    # TRANSLATORS: Please DO NOT translate the word "BridgeDB".
-    4: _("Welcome to BridgeDB!"),
-    # TRANSLATORS: Please DO NOT translate the words "transport" or "TYPE".
-    5: _("Currently supported transport TYPEs:"),
-    6: _("Hey, %s!"),
-    7: _("Hello, friend!"),
-    8: _("Public Keys"),
-    # TRANSLATORS: This string will end up saying something like:
-    # "This email was generated with rainbows, unicorns, and sparkles
-    #  for alice at example.com on Friday, 09 May, 2014 at 18:59:39."
-    9: _("""\
-This email was generated with rainbows, unicorns, and sparkles
-for %s on %s at %s."""),
+If these bridges are not what you need, reply to this email with one of
+the following commands in the message body:"""),
 }
 
 WELCOME = {
@@ -90,11 +77,10 @@ Additionally, BridgeDB has plenty of plain-ol'-vanilla bridges %s without any
 Pluggable Transports %s which maybe doesn't sound as cool, but they can still
 help to circumvent internet censorship in many cases.\n\n"""),
 }
-"""These strings should go on the first "Welcome" email sent by the
-:mod:`~bridgedb.EmailServer`, as well as on the ``index.html`` template used
-by the :mod:`~bridgedb.distributors.https.server`. They are used as an introduction to
-explain what Tor bridges are, what bridges do, and why someone might want to
-use bridges.
+"""These strings should go on the ``options.html`` template used by the
+:mod:`~bridgedb.distributors.https.server`. They are used as an
+introduction to explain what Tor bridges are, what bridges do, and why
+someone might want to use bridges.
 """
 
 FAQ = {
@@ -186,22 +172,21 @@ HOWTO_TBB = {
  Manual explains how you can add your bridges to Tor Browser. If you are
  using Windows, Linux, or OS X, %sclick here%s to learn more. If you
  are using Android, %sclick here%s."""),
+    2: _("""\
+Add these bridges to your Tor Browser by opening your browser
+preferences, clicking on "Tor", and then adding them to the "Provide a
+bridge" field."""),
 }
 
 EMAIL_COMMANDS = {
-    "get help":             _("Displays this message."),
-# TRANSLATORS: Please try to make it clear that "vanilla" here refers to the
-# same non-Pluggable Transport bridges described above as being
-# "plain-ol'-vanilla" bridges.
-    "get bridges":          _("Request vanilla bridges."),
-    "get ipv6":             _("Request IPv6 bridges."),
-    # TRANSLATORS: Please DO NOT translate the word the word "TYPE".
-    "get transport [TYPE]": _("Request a Pluggable Transport by TYPE."),
+    "get bridges":          _("(Request unobfuscated Tor bridges.)"),
+    "get ipv6":             _("(Request IPv6 bridges.)"),
+    # TRANSLATORS: Please DO NOT translate the word "TYPE".
+    "get transport TYPE":   _("(Request obfuscated bridges. Replace TYPE with "
+                              "'obfs4'.)"),
     # TRANSLATORS: Please DO NOT translate "BridgeDB".
     # TRANSLATORS: Please DO NOT translate "GnuPG".
-    "get key":              _("Get a copy of BridgeDB's public GnuPG key."),
-    #"subscribe":            _("Subscribe to receive new bridges once per week"),
-    #"unsubscribe":          _("Cancel a subscription to new bridges"),
+    "get key":              _("(Get a copy of BridgeDB's public GnuPG key.)"),
 }
 
 #-----------------------------------------------------------------------------
diff --git a/bridgedb/test/test_email_autoresponder.py b/bridgedb/test/test_email_autoresponder.py
index c8e9624..d5023e0 100644
--- a/bridgedb/test/test_email_autoresponder.py
+++ b/bridgedb/test/test_email_autoresponder.py
@@ -70,11 +70,12 @@ class CreateResponseBodyTests(unittest.TestCase):
         self.assertSubstring('-----BEGIN PGP PUBLIC KEY BLOCK-----', ret)
 
     def test_createResponseBody_bridges_invalid(self):
-        """An invalid request for 'transport obfs3' should get help text."""
+        """An invalid request for 'transport obfs3' should still return
+        bridges."""
         lines = self._getIncomingLines("testing at localhost")
         lines[4] = "transport obfs3"
         ret = autoresponder.createResponseBody(lines, self.ctx, self.toAddress)
-        self.assertSubstring("COMMANDs", ret)
+        self.assertSubstring("Here are your bridges:", ret)
 
     def test_createResponseBody_bridges_obfs3(self):
         """A request for 'get transport obfs3' should receive a response."""
diff --git a/bridgedb/test/test_email_request.py b/bridgedb/test/test_email_request.py
index 0c87d36..e0bb642 100644
--- a/bridgedb/test/test_email_request.py
+++ b/bridgedb/test/test_email_request.py
@@ -17,26 +17,25 @@ import ipaddr
 
 from twisted.trial import unittest
 
+from bridgedb import strings
 from bridgedb.distributors.email import request
 
 
 class DetermineBridgeRequestOptionsTests(unittest.TestCase):
     """Unittests for :func:`b.e.request.determineBridgeRequestOptions`."""
 
-    def test_determineBridgeRequestOptions_get_help(self):
-        """Requesting 'get help' should raise EmailRequestedHelp."""
-        lines = ['',
-                 'get help']
-        self.assertRaises(request.EmailRequestedHelp,
-                          request.determineBridgeRequestOptions, lines)
-        
-    def test_determineBridgeRequestOptions_get_halp(self):
-        """Requesting 'get halp' should raise EmailRequestedHelp."""
+    def test_determineBridgeRequestOptions_multiline_invalid(self):
         lines = ['',
-                 'get halp']
-        self.assertRaises(request.EmailRequestedHelp,
-                          request.determineBridgeRequestOptions, lines)
-        
+                 'help',
+                 'i need bridges',
+                 'give me your gpgs']
+        reqvest = request.determineBridgeRequestOptions(lines)
+        # We consider every request valid...
+        self.assertEqual(reqvest.isValid(), True)
+        self.assertFalse(reqvest.wantsKey())
+        # ...so by default, we return a bridge.
+        self.assertEqual(len(reqvest.transports), 1)
+
     def test_determineBridgeRequestOptions_get_key(self):
         """Requesting 'get key' should raise EmailRequestedKey."""
         lines = ['',
@@ -45,14 +44,14 @@ class DetermineBridgeRequestOptionsTests(unittest.TestCase):
                           request.determineBridgeRequestOptions, lines)
 
     def test_determineBridgeRequestOptions_multiline_invalid(self):
-        """Requests without a 'get' anywhere should be considered invalid."""
+        """Requests without a 'get' are incorrect but still valid, and should
+        return bridges."""
         lines = ['',
                  'transport obfs3',
                  'ipv6 vanilla bridges',
                  'give me your gpgs']
         reqvest = request.determineBridgeRequestOptions(lines)
-        # It's invalid because it didn't include a 'get' anywhere.
-        self.assertEqual(reqvest.isValid(), False)
+        self.assertEqual(reqvest.isValid(), True)
         self.assertFalse(reqvest.wantsKey())
         # Though they did request IPv6, technically.
         self.assertIs(reqvest.ipVersion, 6)
@@ -61,9 +60,8 @@ class DetermineBridgeRequestOptionsTests(unittest.TestCase):
         self.assertEqual(reqvest.transports[0], 'obfs3')
 
     def test_determineBridgeRequestOptions_multiline_valid(self):
-        """Though requests with a 'get' are considered valid."""
         lines = ['',
-                 'get transport obfs3',
+                 'transport obfs3',
                  'vanilla bridges',
                  'transport scramblesuit unblocked ca']
         reqvest = request.determineBridgeRequestOptions(lines)
@@ -73,6 +71,7 @@ class DetermineBridgeRequestOptionsTests(unittest.TestCase):
         # Though they didn't request IPv6, so it should default to IPv4.
         self.assertIs(reqvest.ipVersion, 4)
         # And they requested two transports.
+        print(reqvest.transports)
         self.assertEqual(len(reqvest.transports), 2)
         self.assertEqual(reqvest.transports[0], 'obfs3')
         self.assertEqual(reqvest.transports[1], 'scramblesuit')
@@ -103,13 +102,16 @@ class DetermineBridgeRequestOptionsTests(unittest.TestCase):
         self.assertEqual(reqvest.notBlockedIn[0], 'ca')
 
     def test_determineBridgeRequestOptions_get_transport(self):
-        """An invalid request for 'transport obfs3' (missing the 'get')."""
+        """An invalid request for 'transprot obfs3' (typo) should return our
+        default bridge."""
+        default_transport = "obfs4"
+        strings._setDefaultTransport(default_transport)
         lines = ['',
-                 'transport obfs3']
+                 'transprot obfs3']
         reqvest = request.determineBridgeRequestOptions(lines)
         self.assertEqual(len(reqvest.transports), 1)
-        self.assertEqual(reqvest.transports[0], 'obfs3')
-        self.assertEqual(reqvest.isValid(), False)
+        self.assertEqual(reqvest.transports[0], default_transport)
+        self.assertEqual(reqvest.isValid(), True)
         
     def test_determineBridgeRequestOptions_get_ipv6(self):
         """An valid request for 'get ipv6'."""
diff --git a/bridgedb/test/test_email_templates.py b/bridgedb/test/test_email_templates.py
index 1d55dd9..4a5979d 100644
--- a/bridgedb/test/test_email_templates.py
+++ b/bridgedb/test/test_email_templates.py
@@ -36,7 +36,7 @@ class EmailTemplatesTests(unittest.TestCase):
         self.offlineFingerprint = '7B78437015E63DF47BB1270ACBD97AA24E8E472E'
 
     def shouldIncludeCommands(self, text):
-        self.assertSubstring('COMMANDs', text)
+        self.assertSubstring('commands', text)
 
     def shouldIncludeInstructions(self, text):
         self.assertSubstring('Tor Browser', text)
@@ -46,10 +46,10 @@ class EmailTemplatesTests(unittest.TestCase):
         self.assertSubstring('Here are your bridges:', text)
 
     def shouldIncludeGreeting(self, text):
-        self.assertSubstring('Hey, blackhole!', text)
+        self.assertSubstring('This is an automated email', text)
 
     def shouldIncludeAutomationNotice(self, text):
-        self.assertSubstring('automated message', text)
+        self.assertSubstring('automated email', text)
 
     def shouldIncludeKey(self, text):
         self.assertSubstring('-----BEGIN PGP PUBLIC KEY BLOCK-----', text)
@@ -59,26 +59,9 @@ class EmailTemplatesTests(unittest.TestCase):
         self.shouldIncludeCommands(text)
 
     def test_templates_addGreeting(self):
-        text = templates.addGreeting(self.t, self.client.local)
+        text = templates.addGreeting(self.t)
         self.shouldIncludeGreeting(text)
 
-    def test_templates_addGreeting_noClient(self):
-        text = templates.addGreeting(self.t, None)
-        self.assertSubstring('Hello, friend!', text)
-
-    def test_templates_addGreeting_withWelcome(self):
-        text = templates.addGreeting(self.t, self.client.local, welcome=True)
-        self.shouldIncludeGreeting(text)
-        self.assertSubstring('Welcome to BridgeDB!', text)
-
-    def test_templates_addGreeting_trueClient(self):
-        text = templates.addGreeting(self.t, True)
-        self.assertSubstring('Hey', text)
-
-    def test_templates_addGreeting_23Client(self):
-        text = templates.addGreeting(self.t, 23)
-        self.assertSubstring('Hey', text)
-
     def test_templates_addHowto(self):
         text = templates.addHowto(self.t)
         self.shouldIncludeInstructions(text)
@@ -97,12 +80,6 @@ class EmailTemplatesTests(unittest.TestCase):
         text = templates.buildKeyMessage(self.t, self.client)
         self.assertSubstring(self.offlineFingerprint, text)
 
-    def test_templates_buildWelcomeText(self):
-        text = templates.buildWelcomeText(self.t, self.client)
-        self.shouldIncludeGreeting(text)
-        self.assertSubstring('Welcome to BridgeDB!', text)
-        self.shouldIncludeCommands(text)
-
     def test_templates_buildSpamWarning(self):
         text = templates.buildSpamWarning(self.t, self.client)
         self.shouldIncludeGreeting(text)





More information about the tor-commits mailing list