Repository: ubclaunchpad/rocket2

Issue:
Slash commands convert user handles to user IDs?
While RTM uses user slack IDs, slash commands don't. Please find a way to resolve this. There are 2 preferred ways:

1. There is an option for slash commands to automagically convert handles to IDs
2. We just use handles in place of IDs, except for RTMs, where we convert the IDs to handles

Comments:

Patch:
diff --git a/command/commands/user.py b/command/commands/user.py
index 84a5307..75f8ef4 100644
--- a/command/commands/user.py
+++ b/command/commands/user.py
@@ -229,7 +229,7 @@ class UserCommand:
 
         :param user_id: Slack ID of user to be added
         :param use_force: If this is set, we store the user even if they are
-        already added in the database
+                          already added in the database
         :return: ``"User added!", 200``
         """
         # Try to look up and avoid overwriting if we are not using force
diff --git a/command/core.py b/command/core.py
index 57d7700..a9c4174 100644
--- a/command/core.py
+++ b/command/core.py
@@ -1,5 +1,6 @@
 """Calls the appropriate handler depending on the event data."""
 from command.commands.user import UserCommand
+import command.util as util
 from model.user import User
 from interface.slack import SlackAPIError
 import logging
@@ -18,17 +19,19 @@ class Core:
         self.__commands["user"] = UserCommand(self.__facade, self.__github)
 
     def handle_app_command(self, cmd_txt, user):
-        """Handle a command call to rocket."""
-        def regularize_char(c):
-            if c == "‘" or c == "’":
-                return "'"
-            if c == '“' or c == '”':
-                return '"'
-            return c
+        """
+        Handle a command call to rocket.
 
+        :param cmd_txt: the command itself
+        :param user: slack ID of user who executed the command
+        :return: tuple where first element is the response text (or a
+                 ``flask.Response`` object), and the second element
+                 is the response status code
+        """
         # Slightly hacky way to deal with Apple platform
         # smart punctuation messing with argparse.
-        cmd_txt = ''.join(map(regularize_char, cmd_txt))
+        cmd_txt = ''.join(map(util.regularize_char, cmd_txt))
+        cmd_txt = util.escaped_id_to_id(cmd_txt)
         s = cmd_txt.split(' ', 1)
         if s[0] == "help" or s[0] is None:
             logging.info("Help command was called")
@@ -40,7 +43,11 @@ class Core:
             return 'Please enter a valid command', 200
 
     def handle_team_join(self, event_data):
-        """Handle the event of a new user joining the workspace."""
+        """
+        Handle the event of a new user joining the workspace.
+
+        :param event_data: JSON event data
+        """
         new_id = event_data["event"]["user"]["id"]
         new_user = User(new_id)
         self.__facade.store_user(new_user)
@@ -52,7 +59,12 @@ class Core:
             logging.error(new_id + " added to database - user not notified")
 
     def get_help(self):
-        """Get help messages and return a formatted string for messaging."""
+        """
+        Get help messages and return a formatted string for messaging.
+
+        :return: Preformatted ``flask.Response`` object containing help
+                 messages
+        """
         message = {"text": "Displaying all available commands. "
                            "To read about a specific command, use "
                            "\n`/rocket [command] help`\n",
diff --git a/command/util.py b/command/util.py
new file mode 100644
index 0000000..0e13756
--- /dev/null
+++ b/command/util.py
@@ -0,0 +1,42 @@
+"""
+Some utility functions.
+
+The following are a few function to help in command handling.
+"""
+import re
+
+
+def regularize_char(c):
+    """
+    Convert any unicode quotation marks to ascii ones.
+
+    Leaves all other characters alone.
+
+    :param c: character to convert
+    :return: ascii equivalent (only quotes are changed)
+    """
+    if c == "‘" or c == "’":
+        return "'"
+    if c == '“' or c == '”':
+        return '"'
+    return c
+
+
+def escaped_id_to_id(s):
+    """
+    Convert an string with escaped IDs to just the IDs.
+
+    Before::
+
+        /rocket user edit --member <@U1143214|su> --name "Steven Universe"
+
+    After::
+
+        /rocket user edit --member U1143214 --name "Steven Universe"
+
+    :param s: string to convert
+    :return: string where all instances of escaped ID is replaced with IDs
+    """
+    return re.sub(r"<@(\w+)\|[^>]+>",
+                  r"\1",
+                  s)
diff --git a/conf.py b/conf.py
index 51b8c1d..a10bce7 100644
--- a/conf.py
+++ b/conf.py
@@ -62,7 +62,7 @@ language = None
 # directories to ignore when looking for source files.
 # This pattern also affects html_static_path and html_extra_path.
 exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'README.md',
-                    '.github/PULL_REQUEST_TEMPLATE.md', '*/README.md']
+                    '.github/*', '*/README.md']
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'emacs'
diff --git a/docs/api/command.rst b/docs/api/command.rst
index a620f2d..79d1b4c 100644
--- a/docs/api/command.rst
+++ b/docs/api/command.rst
@@ -5,8 +5,10 @@ Commands Core
 -------------
 
 .. automodule:: command.core
-    :members:
+   :members:
 
+.. automodule:: command.util
+   :members:
 
 User
 ----


Test patch:
diff --git a/tests/command/util_test.py b/tests/command/util_test.py
new file mode 100644
index 0000000..38d0b46
--- /dev/null
+++ b/tests/command/util_test.py
@@ -0,0 +1,31 @@
+"""Some tests for utility functions in commands utility."""
+import command.util as util
+
+
+def test_regularize_char_standard():
+    """Test how this function reacts to normal operation."""
+    assert util.regularize_char('a') == 'a'
+    assert util.regularize_char(' ') == ' '
+    assert util.regularize_char('\'') == '\''
+    assert util.regularize_char('‘') == '\''
+    assert util.regularize_char('’') == '\''
+    assert util.regularize_char('“') == '"'
+    assert util.regularize_char('”') == '"'
+
+
+def test_escaped_id_conversion():
+    """Test how this function reacts to normal operation."""
+    CMDS = [
+        # Normal operation
+        ('/rocket user edit --member <@U1234|user> --name "User"',
+         '/rocket user edit --member U1234 --name "User"'),
+        # No users
+        ('/rocket user view',
+         '/rocket user view'),
+        # Multiple users
+        ('/rocket foo <@U1234|user> <@U4321|ruse> <@U3412|sure> -h',
+         '/rocket foo U1234 U4321 U3412 -h')
+    ]
+
+    for inp, expect in CMDS:
+        assert util.escaped_id_to_id(inp) == expect