{"action":"created","comment":{"url":"https://api.github.com/repos/serokell/hermetic/pulls/comments/212958186","pull_request_review_id":149691182,"id":212958186,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxMjk1ODE4Ng==","diff_hunk":"@@ -0,0 +1,152 @@\n+alias Hermetic.{YouTrack, Attachment, Slack}\n+\n+defmodule Hermetic.Slash do\n+  @moduledoc \"\"\"\n+  Handles Slack slash commands\n+  \"\"\"\n+\n+  import Plug.Conn\n+\n+  use Plug.ErrorHandler\n+\n+  def init([]), do: []\n+\n+  @doc \"\"\"\n+  Return usage string given a /command\n+  \"\"\"\n+  @spec usage(String.t()) :: String.t()\n+  def usage(command) do\n+    case command do\n+      \"/yt-add\" -> \"projectid [@assignee] [#tag] Title text\"\n+      \"/yt-cmd\" -> \"issue-id command\"\n+    end\n+  end\n+\n+  def handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do\n+    send_resp(conn, 200, \"Usage: \" <> usage(conn.body_params[\"command\"]))\n+  end\n+\n+  @doc \"\"\"\n+  Split off the first word based on whitespace.\n+  Returns a tuple of the first word and the remainder.\n+  \"\"\"\n+  @spec split_word(String.t()) :: [String.t()]\n+  def split_word(string) do\n+    string\n+    |> String.trim_leading()\n+    |> String.split(~r/\\s+|$/, parts: 2)\n+  end\n+\n+  @doc ~S\"\"\"\n+  Split off the first escaped tag (channel) or user or unescaped tag.\n+\n+  ## Examples\n+\n+      iex> split_tag(\"<@U1234|john> remainder\")\n+      {:user, \"U1234\", \"remainder\"}\n+\n+      iex> split_tag(\"<#C1234|channel> remainder\")\n+      {:tag, \"channel\", \"remainder\"}\n+\n+      iex> split_tag(\"#tag remainder\")\n+      {:tag, \"tag\", \"remainder\"}\n+\n+      iex> split_tag(\"nothing\")\n+      {:none, nil, \"nothing\"}\n+  \"\"\"\n+  @spec split_tag(String.t()) :: {:none | :user | :tag, String.t(), String.t()}\n+  def split_tag(string) do\n+    re = ~r/^\\s*(?:(?:\\<\\@([^\\|]+)\\|[^\\>]+\\>)|(?:\\<\\#[^\\|]+\\|([^\\>]+)\\>)|(?:\\#(\\S+)))\\s*(.*)$/\n+    string = String.trim(string)\n+    case Regex.run(re, string) do\n+      [_, user, \"\", \"\", remainder] ->\n+        {:user, user, remainder}\n+      [_, \"\", tag, \"\", remainder] ->\n+        {:tag, tag, remainder}\n+      [_, \"\", \"\", tag, remainder] ->\n+        {:tag, tag, remainder}\n+      nil ->\n+        {:none, \"\", string}\n+    end\n+  end\n+\n+  @doc \"\"\"\n+  Split off #tags and @assignees and return a tuple with them and the\n+  remainder.\n+  \"\"\"\n+  @spec split_tags(String.t(), [String.t()], [String.t()]) :: {[String.t()], [String.t()], String.t()}\n+  def split_tags(string, assignees \\\\ [], tags \\\\ []) do\n+    {type, head, tail} = split_tag(string)\n+    case type do\n+      :user ->\n+        split_tags(tail, assignees ++ [head], tags)\n+      :tag ->\n+        split_tags(tail, assignees, tags ++ [head])\n+      :none ->\n+        {assignees, tags, String.trim(string)}\n+    end\n+  end\n+\n+  @doc \"\"\"\n+  Translate a Slack user id to a YouTrack login name\n+  \"\"\"\n+  @spec translate_user(String.t()) :: String.t()\n+  def translate_user(slack_user) do\n+    YouTrack.emails_to_logins()[Slack.get_email(slack_user)]\n+  end\n+\n+  @doc \"\"\"\n+  Translate any Slack users mentioned to YouTrack logins\n+  \"\"\"\n+  @spec translate_users(String.t()) :: String.t()\n+  def translate_users(command) do\n+    Regex.replace(~r/\\<\\@([^\\|]+)\\|[^\\>]+\\>/, command,\n+      fn _, x -> translate_user(x) end)\n+  end\n+\n+  def call(conn, []) do\n+    case conn.body_params[\"command\"] do\n+      \"/yt-add\" -> yt_add(conn)\n+      \"/yt-cmd\" -> yt_cmd(conn)\n+    end\n+  end\n+\n+  @doc \"\"\"\n+  Handle /yt-add projectid [@assignee] [#tag] Title text\n+  \"\"\"\n+  @spec yt_add(Plug.Conn.t()) :: Plug.Conn.t()\n+  def yt_add(conn) do\n+    [project, rest] = split_word(conn.body_params[\"text\"])\n+    {assignees, tags, title} = split_tags(rest)\n+    issue = YouTrack.create_issue(project, title, \"\")\n+    tag_command = tags\n+                  |> Enum.map(fn x -> \"tag \" <> String.replace(x, \"_\", \" \") end)","path":"lib/hermetic/slash.ex","position":123,"original_position":123,"commit_id":"480f93cd2b3c9005d56ffa03406385b1058d6303","original_commit_id":"480f93cd2b3c9005d56ffa03406385b1058d6303","user":{"login":"Lucus16","id":2487922,"node_id":"MDQ6VXNlcjI0ODc5MjI=","avatar_url":"https://avatars1.githubusercontent.com/u/2487922?v=4","gravatar_id":"","url":"https://api.github.com/users/Lucus16","html_url":"https://github.com/Lucus16","followers_url":"https://api.github.com/users/Lucus16/followers","following_url":"https://api.github.com/users/Lucus16/following{/other_user}","gists_url":"https://api.github.com/users/Lucus16/gists{/gist_id}","starred_url":"https://api.github.com/users/Lucus16/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Lucus16/subscriptions","organizations_url":"https://api.github.com/users/Lucus16/orgs","repos_url":"https://api.github.com/users/Lucus16/repos","events_url":"https://api.github.com/users/Lucus16/events{/privacy}","received_events_url":"https://api.github.com/users/Lucus16/received_events","type":"User","site_admin":false},"body":"YouTrack has tags with spaces, but parsing tags with spaces would be a major pain. This allows you to name those tags using underscores.","created_at":"2018-08-27T12:35:01Z","updated_at":"2018-08-27T12:35:01Z","html_url":"https://github.com/serokell/hermetic/pull/4#discussion_r212958186","pull_request_url":"https://api.github.com/repos/serokell/hermetic/pulls/4","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/serokell/hermetic/pulls/comments/212958186"},"html":{"href":"https://github.com/serokell/hermetic/pull/4#discussion_r212958186"},"pull_request":{"href":"https://api.github.com/repos/serokell/hermetic/pulls/4"}},"in_reply_to_id":212850299},"pull_request":{"url":"https://api.github.com/repos/serokell/hermetic/pulls/4","id":210714587,"node_id":"MDExOlB1bGxSZXF1ZXN0MjEwNzE0NTg3","html_url":"https://github.com/serokell/hermetic/pull/4","diff_url":"https://github.com/serokell/hermetic/pull/4.diff","patch_url":"https://github.com/serokell/hermetic/pull/4.patch","issue_url":"https://api.github.com/repos/serokell/hermetic/issues/4","number":4,"state":"open","locked":false,"title":"Implement /yt-add and /yt-cmd; config and indent cleanup","user":{"login":"Lucus16","id":2487922,"node_id":"MDQ6VXNlcjI0ODc5MjI=","avatar_url":"https://avatars1.githubusercontent.com/u/2487922?v=4","gravatar_id":"","url":"https://api.github.com/users/Lucus16","html_url":"https://github.com/Lucus16","followers_url":"https://api.github.com/users/Lucus16/followers","following_url":"https://api.github.com/users/Lucus16/following{/other_user}","gists_url":"https://api.github.com/users/Lucus16/gists{/gist_id}","starred_url":"https://api.github.com/users/Lucus16/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Lucus16/subscriptions","organizations_url":"https://api.github.com/users/Lucus16/orgs","repos_url":"https://api.github.com/users/Lucus16/repos","events_url":"https://api.github.com/users/Lucus16/events{/privacy}","received_events_url":"https://api.github.com/users/Lucus16/received_events","type":"User","site_admin":false},"body":"There are still a few issues with tags, but using existing, globally settable tags should work fine.","created_at":"2018-08-24T11:31:01Z","updated_at":"2018-08-27T12:35:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d31b1ba5d12c34d67d00260a1c583f51e6e1e954","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/serokell/hermetic/pulls/4/commits","review_comments_url":"https://api.github.com/repos/serokell/hermetic/pulls/4/comments","review_comment_url":"https://api.github.com/repos/serokell/hermetic/pulls/comments{/number}","comments_url":"https://api.github.com/repos/serokell/hermetic/issues/4/comments","statuses_url":"https://api.github.com/repos/serokell/hermetic/statuses/480f93cd2b3c9005d56ffa03406385b1058d6303","head":{"label":"serokell:mkissue","ref":"mkissue","sha":"480f93cd2b3c9005d56ffa03406385b1058d6303","user":{"login":"serokell","id":13840520,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzODQwNTIw","avatar_url":"https://avatars2.githubusercontent.com/u/13840520?v=4","gravatar_id":"","url":"https://api.github.com/users/serokell","html_url":"https://github.com/serokell","followers_url":"https://api.github.com/users/serokell/followers","following_url":"https://api.github.com/users/serokell/following{/other_user}","gists_url":"https://api.github.com/users/serokell/gists{/gist_id}","starred_url":"https://api.github.com/users/serokell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/serokell/subscriptions","organizations_url":"https://api.github.com/users/serokell/orgs","repos_url":"https://api.github.com/users/serokell/repos","events_url":"https://api.github.com/users/serokell/events{/privacy}","received_events_url":"https://api.github.com/users/serokell/received_events","type":"Organization","site_admin":false},"repo":{"id":141491790,"node_id":"MDEwOlJlcG9zaXRvcnkxNDE0OTE3OTA=","name":"hermetic","full_name":"serokell/hermetic","owner":{"login":"serokell","id":13840520,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzODQwNTIw","avatar_url":"https://avatars2.githubusercontent.com/u/13840520?v=4","gravatar_id":"","url":"https://api.github.com/users/serokell","html_url":"https://github.com/serokell","followers_url":"https://api.github.com/users/serokell/followers","following_url":"https://api.github.com/users/serokell/following{/other_user}","gists_url":"https://api.github.com/users/serokell/gists{/gist_id}","starred_url":"https://api.github.com/users/serokell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/serokell/subscriptions","organizations_url":"https://api.github.com/users/serokell/orgs","repos_url":"https://api.github.com/users/serokell/repos","events_url":"https://api.github.com/users/serokell/events{/privacy}","received_events_url":"https://api.github.com/users/serokell/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/serokell/hermetic","description":"Slack bot that links to YouTrack issues","fork":false,"url":"https://api.github.com/repos/serokell/hermetic","forks_url":"https://api.github.com/repos/serokell/hermetic/forks","keys_url":"https://api.github.com/repos/serokell/hermetic/keys{/key_id}","collaborators_url":"https://api.github.com/repos/serokell/hermetic/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/serokell/hermetic/teams","hooks_url":"https://api.github.com/repos/serokell/hermetic/hooks","issue_events_url":"https://api.github.com/repos/serokell/hermetic/issues/events{/number}","events_url":"https://api.github.com/repos/serokell/hermetic/events","assignees_url":"https://api.github.com/repos/serokell/hermetic/assignees{/user}","branches_url":"https://api.github.com/repos/serokell/hermetic/branches{/branch}","tags_url":"https://api.github.com/repos/serokell/hermetic/tags","blobs_url":"https://api.github.com/repos/serokell/hermetic/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/serokell/hermetic/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/serokell/hermetic/git/refs{/sha}","trees_url":"https://api.github.com/repos/serokell/hermetic/git/trees{/sha}","statuses_url":"https://api.github.com/repos/serokell/hermetic/statuses/{sha}","languages_url":"https://api.github.com/repos/serokell/hermetic/languages","stargazers_url":"https://api.github.com/repos/serokell/hermetic/stargazers","contributors_url":"https://api.github.com/repos/serokell/hermetic/contributors","subscribers_url":"https://api.github.com/repos/serokell/hermetic/subscribers","subscription_url":"https://api.github.com/repos/serokell/hermetic/subscription","commits_url":"https://api.github.com/repos/serokell/hermetic/commits{/sha}","git_commits_url":"https://api.github.com/repos/serokell/hermetic/git/commits{/sha}","comments_url":"https://api.github.com/repos/serokell/hermetic/comments{/number}","issue_comment_url":"https://api.github.com/repos/serokell/hermetic/issues/comments{/number}","contents_url":"https://api.github.com/repos/serokell/hermetic/contents/{+path}","compare_url":"https://api.github.com/repos/serokell/hermetic/compare/{base}...{head}","merges_url":"https://api.github.com/repos/serokell/hermetic/merges","archive_url":"https://api.github.com/repos/serokell/hermetic/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/serokell/hermetic/downloads","issues_url":"https://api.github.com/repos/serokell/hermetic/issues{/number}","pulls_url":"https://api.github.com/repos/serokell/hermetic/pulls{/number}","milestones_url":"https://api.github.com/repos/serokell/hermetic/milestones{/number}","notifications_url":"https://api.github.com/repos/serokell/hermetic/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/serokell/hermetic/labels{/name}","releases_url":"https://api.github.com/repos/serokell/hermetic/releases{/id}","deployments_url":"https://api.github.com/repos/serokell/hermetic/deployments","created_at":"2018-07-18T21:23:59Z","updated_at":"2018-08-20T14:31:47Z","pushed_at":"2018-08-24T11:31:02Z","git_url":"git://github.com/serokell/hermetic.git","ssh_url":"git@github.com:serokell/hermetic.git","clone_url":"https://github.com/serokell/hermetic.git","svn_url":"https://github.com/serokell/hermetic","homepage":"","size":77,"stargazers_count":0,"watchers_count":0,"language":"Elixir","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"serokell:master","ref":"master","sha":"25cc94824fc01de96a96b5f15d902685e378b31d","user":{"login":"serokell","id":13840520,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzODQwNTIw","avatar_url":"https://avatars2.githubusercontent.com/u/13840520?v=4","gravatar_id":"","url":"https://api.github.com/users/serokell","html_url":"https://github.com/serokell","followers_url":"https://api.github.com/users/serokell/followers","following_url":"https://api.github.com/users/serokell/following{/other_user}","gists_url":"https://api.github.com/users/serokell/gists{/gist_id}","starred_url":"https://api.github.com/users/serokell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/serokell/subscriptions","organizations_url":"https://api.github.com/users/serokell/orgs","repos_url":"https://api.github.com/users/serokell/repos","events_url":"https://api.github.com/users/serokell/events{/privacy}","received_events_url":"https://api.github.com/users/serokell/received_events","type":"Organization","site_admin":false},"repo":{"id":141491790,"node_id":"MDEwOlJlcG9zaXRvcnkxNDE0OTE3OTA=","name":"hermetic","full_name":"serokell/hermetic","owner":{"login":"serokell","id":13840520,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzODQwNTIw","avatar_url":"https://avatars2.githubusercontent.com/u/13840520?v=4","gravatar_id":"","url":"https://api.github.com/users/serokell","html_url":"https://github.com/serokell","followers_url":"https://api.github.com/users/serokell/followers","following_url":"https://api.github.com/users/serokell/following{/other_user}","gists_url":"https://api.github.com/users/serokell/gists{/gist_id}","starred_url":"https://api.github.com/users/serokell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/serokell/subscriptions","organizations_url":"https://api.github.com/users/serokell/orgs","repos_url":"https://api.github.com/users/serokell/repos","events_url":"https://api.github.com/users/serokell/events{/privacy}","received_events_url":"https://api.github.com/users/serokell/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/serokell/hermetic","description":"Slack bot that links to YouTrack issues","fork":false,"url":"https://api.github.com/repos/serokell/hermetic","forks_url":"https://api.github.com/repos/serokell/hermetic/forks","keys_url":"https://api.github.com/repos/serokell/hermetic/keys{/key_id}","collaborators_url":"https://api.github.com/repos/serokell/hermetic/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/serokell/hermetic/teams","hooks_url":"https://api.github.com/repos/serokell/hermetic/hooks","issue_events_url":"https://api.github.com/repos/serokell/hermetic/issues/events{/number}","events_url":"https://api.github.com/repos/serokell/hermetic/events","assignees_url":"https://api.github.com/repos/serokell/hermetic/assignees{/user}","branches_url":"https://api.github.com/repos/serokell/hermetic/branches{/branch}","tags_url":"https://api.github.com/repos/serokell/hermetic/tags","blobs_url":"https://api.github.com/repos/serokell/hermetic/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/serokell/hermetic/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/serokell/hermetic/git/refs{/sha}","trees_url":"https://api.github.com/repos/serokell/hermetic/git/trees{/sha}","statuses_url":"https://api.github.com/repos/serokell/hermetic/statuses/{sha}","languages_url":"https://api.github.com/repos/serokell/hermetic/languages","stargazers_url":"https://api.github.com/repos/serokell/hermetic/stargazers","contributors_url":"https://api.github.com/repos/serokell/hermetic/contributors","subscribers_url":"https://api.github.com/repos/serokell/hermetic/subscribers","subscription_url":"https://api.github.com/repos/serokell/hermetic/subscription","commits_url":"https://api.github.com/repos/serokell/hermetic/commits{/sha}","git_commits_url":"https://api.github.com/repos/serokell/hermetic/git/commits{/sha}","comments_url":"https://api.github.com/repos/serokell/hermetic/comments{/number}","issue_comment_url":"https://api.github.com/repos/serokell/hermetic/issues/comments{/number}","contents_url":"https://api.github.com/repos/serokell/hermetic/contents/{+path}","compare_url":"https://api.github.com/repos/serokell/hermetic/compare/{base}...{head}","merges_url":"https://api.github.com/repos/serokell/hermetic/merges","archive_url":"https://api.github.com/repos/serokell/hermetic/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/serokell/hermetic/downloads","issues_url":"https://api.github.com/repos/serokell/hermetic/issues{/number}","pulls_url":"https://api.github.com/repos/serokell/hermetic/pulls{/number}","milestones_url":"https://api.github.com/repos/serokell/hermetic/milestones{/number}","notifications_url":"https://api.github.com/repos/serokell/hermetic/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/serokell/hermetic/labels{/name}","releases_url":"https://api.github.com/repos/serokell/hermetic/releases{/id}","deployments_url":"https://api.github.com/repos/serokell/hermetic/deployments","created_at":"2018-07-18T21:23:59Z","updated_at":"2018-08-20T14:31:47Z","pushed_at":"2018-08-24T11:31:02Z","git_url":"git://github.com/serokell/hermetic.git","ssh_url":"git@github.com:serokell/hermetic.git","clone_url":"https://github.com/serokell/hermetic.git","svn_url":"https://github.com/serokell/hermetic","homepage":"","size":77,"stargazers_count":0,"watchers_count":0,"language":"Elixir","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/serokell/hermetic/pulls/4"},"html":{"href":"https://github.com/serokell/hermetic/pull/4"},"issue":{"href":"https://api.github.com/repos/serokell/hermetic/issues/4"},"comments":{"href":"https://api.github.com/repos/serokell/hermetic/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/serokell/hermetic/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/serokell/hermetic/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/serokell/hermetic/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/serokell/hermetic/statuses/480f93cd2b3c9005d56ffa03406385b1058d6303"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"Lucus16"}}