private void processAssigned(GithubEvent event) {
    if (config.hasUserMappings()) {
      GithubEvent.User ghUser = event.getAssignee();
      String jiraUser = config.getJiraUser(ghUser.getLogin());

      if (jiraUser != null) {
        // Get the current issue from JIRA
        String title = event.getIssue().getTitle();
        Matcher m = jiraIssuePattern.matcher(title);
        if (m.find()) {
          JiraConnector conn = new JiraConnector(config);
          GetIssue get = new GetIssue.Builder().withIssueKey(m.group(1)).build();
          try {
            JiraEvent.Issue issue = conn.execute(get);
            String jiraCurrentAssignee = issue.getAssignee();

            if (event.getAction().equals(GITHUB_UNASSIGNED)) {
              if (jiraCurrentAssignee != null && jiraCurrentAssignee.equals(jiraUser)) {
                // Update Jira issue with no one assigned
                UpdateIssue update =
                    new UpdateIssue.Builder()
                        .withJiraIssueKey(m.group(1))
                        .withAssignee(UpdateIssue.NO_ASSIGNEE)
                        .build();

                conn.execute(update);
              }
            } else {
              if (jiraCurrentAssignee == null || !jiraCurrentAssignee.equals(jiraUser)) {
                UpdateIssue update =
                    new UpdateIssue.Builder()
                        .withJiraIssueKey(m.group(1))
                        .withAssignee(jiraUser)
                        .build();

                conn.execute(update);
              }
            }
          } catch (ExecutionException ex) {
            Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
      }
    }
  }
  private String processOpenedEvent(GithubEvent event) {
    JiraConnector conn = new JiraConnector(config);
    String jiraIssueKey = null;

    if (event.hasIssue()) {
      String title = event.getIssue().getTitle();
      Matcher m = jiraIssuePattern.matcher(title);
      if (m.find()) {
        // Originated from JIRA, need to update JIRA with issue number and external link

        String githubIssueField = config.getJira().getGithubIssueNumberField();
        jiraIssueKey = m.group(1);

        UpdateIssue update =
            new UpdateIssue.Builder()
                .withCustomField(githubIssueField, event.getIssue().getNumber())
                .withJiraIssueKey(jiraIssueKey)
                .build();
        try {
          conn.execute(update);
          createExternalLink(conn, jiraIssueKey, event);
        } catch (ExecutionException ex) {
          Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
        }
      } else {
        // Originated in github. Create in JIRA and add external link

        String githubIssueField = config.getJira().getGithubIssueNumberField();
        String jiraRepoField = config.getJira().getGithubRepoNameField();
        ServiceConfig.Repository repo =
            config.getRepoForGithubName(event.getRepository().getName());

        String jiraProjectKey = repo.getJiraProjectKey();
        String jiraRepoName = repo.getJiraName();
        int githubIssueNumber = event.getIssue().getNumber();

        String body =
            event.getIssue().getBody()
                + "\n\n[Created in Github by "
                + event.getIssue().getUser().getLogin()
                + " ]";

        /*
         * If we're mapping milestones to eipcs, have some
         * work to do here.
         */
        String epicJiraKey = null;
        if (repo.mapEpicsToMilestones() && event.getIssue().hasMilestone()) {
          Milestone ms = event.getIssue().getMilestone();
          String epicName = ms.getTitle();

          // Of course, they can't make this easy. Querying custom fields
          // requires a format of cf[xxxx] rather than, you know, the field
          // name.
          m = extractCustomFieldNumber.matcher(config.getJira().getEpicNameField());
          m.find();
          String cfNumber = m.group(1);
          String jql =
              "project = " + jiraProjectKey + " and cf[" + cfNumber + "] = \"" + epicName + "\"";

          SearchIssues search = new SearchIssues.Builder().withJQL(jql).build();
          try {
            List<JiraEvent.Issue> epicList = conn.execute(search);
            // Should only return one or zero.
            if (!epicList.isEmpty()) {
              JiraEvent.Issue epic = epicList.get(0);
              epicJiraKey = epic.getJiraIssueKey();
            }

          } catch (ExecutionException ex) {
            Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
          }
        }

        CreateIssue.Builder builder =
            new CreateIssue.Builder()
                .withProjectKey(jiraProjectKey)
                .withIssuetype("Task")
                .withSummary(event.getIssue().getTitle())
                .withDescription(body)
                .withCustomField(githubIssueField, githubIssueNumber)
                .withCustomField(jiraRepoField, "value", jiraRepoName);

        // populate any custom fields from repo config
        for (ServiceConfig.Repository.JiraField field : repo.getJiraFields()) {
          if (field.getType().equals("object")) {
            builder.withCustomField(field.getName(), field.getKey(), field.getValue());
          } else {
            builder.withCustomField(field.getName(), field.getValue());
          }
        }

        if (epicJiraKey != null) {
          builder.withCustomField(config.getJira().getEpicLinkField(), epicJiraKey);
        }

        if (repo.labelVersions()) {
          List<String> fixVersions = new LinkedList<>();
          List<String> affectsVersions = new LinkedList<>();
          for (GithubEvent.Issue.Label label : event.getIssue().getLabels()) {
            m = extractFixedVersion.matcher(label.getName());
            if (m.find()) {
              fixVersions.add(m.group(1));
            } else {
              m = extractAffectsVersion.matcher(label.getName());
              if (m.find()) {
                affectsVersions.add(m.group(1));
              }
            }
          }
          builder.withAffectsVersions(affectsVersions).withFixVersions(fixVersions);
        }

        if (config.hasUserMappings() && event.getIssue().hasAssignee()) {
          String jiraAssignee = config.getJiraUser(event.getIssue().getAssignee().getLogin());

          builder.withAssignee(jiraAssignee);
        }

        try {
          jiraIssueKey = conn.execute(builder.build());
          createExternalLink(conn, jiraIssueKey, event);
        } catch (ExecutionException ex) {
          Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
    } else if (event.hasPullRequest()) {
      linkPullRequestToIssue(conn, event);
    }

    return jiraIssueKey;
  }
  private void processLabeled(GithubEvent event) {
    // Should always be true but ... meh.
    if (event.hasLabel() && event.hasIssue()) {
      // We're only looking for Version labels here.
      GithubEvent.Issue.Label label = event.getLabel();

      if (label.getName().startsWith("Fixed in:") || label.getName().startsWith("Affects:")) {
        String title = event.getIssue().getTitle();
        Matcher m = jiraIssuePattern.matcher(title);
        if (m.find()) {
          String jiraIssueKey = m.group(1);

          // Git the Jira issue and check the versions
          JiraConnector conn = new JiraConnector(config);

          GetIssue get = new GetIssue.Builder().withIssueKey(jiraIssueKey).build();
          try {
            JiraEvent.Issue jiraIssue = conn.execute(get);

            m = extractFixedVersion.matcher(label.getName());
            if (m.find()) {
              String version = m.group(1);
              if (event.getAction().equals(GITHUB_LABELED)
                  && !jiraIssue.getFixVersions().contains(version)) {
                UpdateVersionsOnIssue update =
                    new UpdateVersionsOnIssue.Builder()
                        .withIssueKey(jiraIssueKey)
                        .addFixVersion(version)
                        .build();

                conn.execute(update);
              } else if (event.getAction().equals(GITHUB_UNLABELED)
                  && jiraIssue.getFixVersions().contains(version)) {
                UpdateVersionsOnIssue update =
                    new UpdateVersionsOnIssue.Builder()
                        .withIssueKey(jiraIssueKey)
                        .removeFixVersion(version)
                        .build();

                conn.execute(update);
              }

            } else {
              m = extractAffectsVersion.matcher(label.getName());
              if (m.find()) {
                String version = m.group(1);
                if (event.getAction().equals(GITHUB_LABELED)
                    && !jiraIssue.getAffectsVersions().contains(version)) {
                  UpdateVersionsOnIssue update =
                      new UpdateVersionsOnIssue.Builder()
                          .withIssueKey(jiraIssueKey)
                          .addAffectsVersion(version)
                          .build();

                  conn.execute(update);
                } else if (event.getAction().equals(GITHUB_UNLABELED)
                    && jiraIssue.getAffectsVersions().contains(version)) {
                  UpdateVersionsOnIssue update =
                      new UpdateVersionsOnIssue.Builder()
                          .withIssueKey(jiraIssueKey)
                          .removeAffectsVersion(version)
                          .build();

                  conn.execute(update);
                }
              }
            }

          } catch (ExecutionException ex) {
            Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
      }
    }
  }
  private void linkPullRequestToIssue(JiraConnector conn, GithubEvent event) {
    // PR created, see if it mentions a GH isse or JIRA issue
    // If it does, update in JIRA.
    String body;
    if (event.hasPullRequest()) {
      body = event.getPullRequest().getBody();
    } else // It's a pull request comment, disguised as an issue (wrapped in an Enigma)
    {
      body = event.getComment().getBody();
    }

    List<String> ghIssueNumbers = scanForGithubIssueMentions(body);
    List<String> directJiraMentions = scanForJiraIssueMentions(body);

    // For GH issues, we have to query JIRA and get back the issue
    // keys that have the issue in the github issue id field and add those
    // to the jira keys.
    List<String> ghIssueMentions = new LinkedList<>();

    // Of course, they can't make this easy. Querying custom fields
    // requires a format of cf[xxxx] rather than, you know, the field
    // name.
    Matcher m = extractCustomFieldNumber.matcher(config.getJira().getGithubIssueNumberField());
    m.find();
    String cfNumber = m.group(1);
    ServiceConfig.Repository repo = config.getRepoForGithubName(event.getRepository().getName());
    String jiraProjectKey = repo.getJiraProjectKey();

    Map<String, String> jiraKeyToGhNum = new HashMap<>();

    for (String ghIssueNum : ghIssueNumbers) {
      String jql = "project = " + jiraProjectKey + " and cf[" + cfNumber + "] = " + ghIssueNum;

      SearchIssues search = new SearchIssues.Builder().withJQL(jql).build();
      try {
        List<JiraEvent.Issue> issues = conn.execute(search);
        for (JiraEvent.Issue issue : issues) {
          ghIssueMentions.add(issue.getJiraIssueKey());
          jiraKeyToGhNum.put(issue.getJiraIssueKey(), ghIssueNum);
        }
      } catch (ExecutionException ex) {
        Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
      }
    }

    // Now we have all the JIRA issues mentioned in this PR, either
    // directly or indirectly. Update them with the link to the PR
    List<String> jiraIssueKeys = new LinkedList<>();
    jiraIssueKeys.addAll(directJiraMentions);
    jiraIssueKeys.addAll(ghIssueMentions);
    for (String jKey : jiraIssueKeys) {
      try {
        createExternalLink(conn, jKey, event);
      } catch (ExecutionException ex) {
        Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
      }
    }

    // For direct JIRA mentions, we want to update the PR
    // with the GH issue #. For GH Issue nums, the JIRA key. Unfortunately when
    // you add an external link to a JIRA issue it doesn't send an
    // issue update out. It seems as though editing a PR in GH doesn't
    // send out a update notice which is kinda annoying on one hand,
    // but should work well here.

    GithubConnector ghConn = new GithubConnector(config);

    for (String jKey : jiraIssueKeys) {
      try {
        if (jiraKeyToGhNum.containsKey(jKey)) {
          String ghIssueNum = jiraKeyToGhNum.get(jKey);
          body = body.replace("#" + ghIssueNum, "#" + ghIssueNum + " (" + jKey + ")");
        } else {
          // Get GH issue number from issue in JIRA
          GetIssue get = new GetIssue.Builder().withIssueKey(jKey).build();
          JiraEvent.Issue issue = conn.execute(get);
          // update this PR body with the GH issue number
          if (issue.hasGithubIssueNumber(config)) {
            body = body.replace(jKey, jKey + " (#" + issue.getGithubIssueNumber(config) + ")");
          }
        }

        if (event.hasPullRequest()) {
          UpdatePullRequest update =
              new UpdatePullRequest.Builder()
                  .withRepository(repo)
                  .withBody(body)
                  .withPullRequestNumber(event.getPullRequest().getNumber())
                  .build();

          ghConn.execute(update);

        } else // pull request comment
        {
          ModifyComment modify =
              new ModifyComment.Builder()
                  .withBody(body)
                  .withRepository(repo)
                  .withCommentId(event.getComment().getId())
                  .build();

          ghConn.execute(modify);
        }
      } catch (ExecutionException ex) {
        Logger.getLogger(GithubWebhook.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
  }