private void version(String channel) {
     try {
         String v = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("version.txt"));
         sendMessage(channel,"My version is "+v);
     } catch (IOException e) {
         e.printStackTrace();
         sendMessage(channel,"I don't know who I am");
     }
 }
    private void removeAutoVoice(String channel, String sender, String target) {
      if (!isSenderAuthorized(channel,sender)) {
        insufficientPermissionError(channel);
        return;
      }

      sendMessage("CHANSERV", "flags " + channel + " " + target + " -V");
      sendMessage("CHANSERV", "devoice " + channel + " " + target);
      sendMessage(channel, "Voice priviledge (-V) removed for " + target);
    }
    private void createGitHubRepository(String channel, String name, String collaborator) {
        try {
            GitHub github = GitHub.connect();
            GHOrganization org = github.getOrganization("jenkinsci");
            GHRepository r = org.createRepository(name, "", "", "Everyone", true);
            setupRepository(r);

            GHTeam t = getOrCreateRepoLocalTeam(org, r);
            if (collaborator!=null)
                t.add(github.getUser(collaborator));

            sendMessage(channel,"New github repository created at "+r.getUrl());
        } catch (IOException e) {
            sendMessage(channel,"Failed to create a repository: "+e.getMessage());
            e.printStackTrace();
        }
    }
 /**
  * Creates an issue tracker component.
  */
 private void setDefaultAssignee(String channel, String sender, String subcomponent, String owner) {
     if (!isSenderAuthorized(channel,sender)) {
         insufficientPermissionError(channel);
         return;
     }
     
     sendMessage(channel,String.format("Changing default assignee of subcomponent %s to %s",subcomponent,owner));
     
     try {
         JiraScraper js = new JiraScraper();
         js.setDefaultAssignee("JENKINS", subcomponent, AssigneeType.COMPONENT_LEAD, owner);
         sendMessage(channel,"Default assignee set to " + owner);
     } catch (Exception e) {
         sendMessage(channel,"Failed to set default assignee: "+e.getMessage());
         e.printStackTrace();
     }
 }
    /**
     * Creates an issue tracker component.
     */
    private void createComponent(String channel, String sender, String subcomponent, String owner) {
        if (!isSenderAuthorized(channel,sender)) {
            insufficientPermissionError(channel);
            return;
        }

        sendMessage(channel,String.format("Adding a new subcomponent %s to the bug tracker, owned by %s",subcomponent,owner));

        try {
            JiraScraper js = new JiraScraper();
            js.createComponent("JENKINS", subcomponent, owner, AssigneeType.COMPONENT_LEAD);
            sendMessage(channel,"New component created");
        } catch (Exception e) {
            sendMessage(channel,"Failed to create a new component: "+e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * @param newName
     *      If not null, rename a epository after a fork.
     */
    private void forkGitHub(String channel, String owner, String repo, String newName) {
        try {
            sendMessage(channel, "Forking "+repo);

            GitHub github = GitHub.connect();
            GHUser user = github.getUser(owner);
            if (user==null) {
                sendMessage(channel,"No such user: "******"No such repository: "+repo);
                return;
            }

            GHOrganization org = github.getOrganization("jenkinsci");
            GHRepository r;
            try {
                r = orig.forkTo(org);
            } catch (IOException e) {
                // we started seeing 500 errors, presumably due to time out.
                // give it a bit of time, and see if the repository is there
                System.out.println("GitHub reported that it failed to fork "+owner+"/"+repo+". But we aren't trusting");
                Thread.sleep(3000);
                r = org.getRepository(repo);
                if (r==null)
                    throw e;
            }
            if (newName!=null)
                r.renameTo(newName);

            // GitHub adds a lot of teams to this repo by default, which we don't want
            Set<GHTeam> legacyTeams = r.getTeams();

            GHTeam t = getOrCreateRepoLocalTeam(org, r);
            t.add(user);    // the user immediately joins this team

            // the Everyone group gets access to this new repository, too.
            GHTeam everyone = org.getTeams().get("Everyone");
            everyone.add(r);

            setupRepository(r);

            sendMessage(channel, "Created https://github.com/jenkinsci/" + (newName != null ? newName : repo));

            // remove all the existing teams
            for (GHTeam team : legacyTeams)
                team.remove(r);

        } catch (InterruptedException e) {
            sendMessage(channel,"Failed to fork a repository: "+e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            sendMessage(channel,"Failed to fork a repository: "+e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * Adds a new collaborator to existing repositories.
     *
     * @param justForThisRepo
     *      Null to add to "Everyone", otherwise add him to a team specific repository.
     */
    private void addGitHubCommitter(String channel, String sender, String collaborator, String justForThisRepo) {
        if (!isSenderAuthorized(channel,sender)) {
            insufficientPermissionError(channel);
            return;
        }
        try {
            GitHub github = GitHub.connect();
            GHUser c = github.getUser(collaborator);
            GHOrganization o = github.getOrganization("jenkinsci");
            GHTeam t = justForThisRepo==null ? o.getTeams().get("Everyone") 
                                             : getOrCreateRepoLocalTeam(o, o.getRepository(justForThisRepo));
            if (t==null) {
                sendMessage(channel,"No team for "+justForThisRepo);
                return;
            }

            t.add(c);
            o.publicize(c);
            sendMessage(channel,"Added "+collaborator+" as a GitHub committer");
        } catch (IOException e) {
            sendMessage(channel,"Failed to create a repository: "+e.getMessage());
            e.printStackTrace();
        }
    }
    private void replyBugStatus(String channel, String number) {
        Long time = (Long)recentIssues.get(number);

        recentIssues.put(number,System.currentTimeMillis());

        if (time!=null) {
            if (System.currentTimeMillis()-time < 60*1000) {
                return; // already mentioned recently. don't repeat
            }
        }

        try {
            sendMessage(channel, getSummary(number));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    protected void onMessage(String channel, String sender, String login, String hostname, String message) {
        if (!channel.equals("#jenkins"))     return; // not in this channel
        if (sender.equals("jenkinsci_builds"))   return; // ignore messages from other bots

        message = message.trim();
        
        String prefix = getNick() + ":";
        if (!message.startsWith(prefix)) {
            // not send to me
            Matcher m = Pattern.compile("(?:hudson-|jenkins-|bug )([0-9]+)",CASE_INSENSITIVE).matcher(message);
            while (m.find()) {
                replyBugStatus(channel,m.group(1));
            }
            return;
        }

        String payload = message.substring(prefix.length(), message.length()).trim();
        // replace duplicate whitespace with a single space
        payload = payload.replaceAll("\\s+", " ");
        Matcher m;

        m = Pattern.compile("(?:create|make|add) (\\S+)(?: repository)? (?:on|in) github(?: for (\\S+))?",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            createGitHubRepository(channel, m.group(1), m.group(2));
            return;
        }

        m = Pattern.compile("fork (\\S+)/(\\S+)(?: on github)?(?: as (\\S+))?",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            forkGitHub(channel, m.group(1),m.group(2),m.group(3));
            return;
        }

        m = Pattern.compile("(?:make|give|grant|add) (\\S+)(?: as)? (?:a )?(?:committ?er|commit access) (?:of|on|to|at) (\\S+)",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            addGitHubCommitter(channel,sender,m.group(1),m.group(2));
            return;
        }

        m = Pattern.compile("(?:make|give|grant|add) (\\S+)(?: as)? (a )?(committ?er|commit access).*",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            addGitHubCommitter(channel,sender,m.group(1),null);
            return;
        }

        m = Pattern.compile("(?:create|make|add) (\\S+)(?: component)? in (?:the )?(?:issue|bug)(?: tracker| database)? for (\\S+)",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            createComponent(channel, sender, m.group(1), m.group(2));
            return;
        }

        m = Pattern.compile("(?:make|set) (\\S+) (?:the |as )?(?:lead|default assignee) (?:for|of) (\\S+)",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            setDefaultAssignee(channel, sender, m.group(2), m.group(1));
            return;
        }
        
        m = Pattern.compile("(?:make|give|grant|add) (\\S+) voice(?: on irc)?",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            grantAutoVoice(channel,sender,m.group(1));
            return;
        }

        m = Pattern.compile("(?:rem|remove|ungrant|del|delete) (\\S+) voice(?: on irc)?",CASE_INSENSITIVE).matcher(payload);
        if (m.matches()) {
            removeAutoVoice(channel,sender,m.group(1));
            return;
        }

        if (payload.equalsIgnoreCase("version")) {
            version(channel);
            return;
        }

        if (payload.equalsIgnoreCase("help")) {
            help(channel);
            return;
        }

        if (payload.equalsIgnoreCase("refresh")) {
            // get the updated list
            sendRawLine("NAMES #jenkins");
            return;
        }

        sendMessage(channel,"I didn't understand the command");

        try {
            PrintWriter w = new PrintWriter(new FileWriter(unknownCommands, true));
            w.println(payload);
            w.close();
        } catch (IOException e) {// if we fail to write, let it be.
            e.printStackTrace();
        }
    }
 private void insufficientPermissionError(String channel) {
     sendMessage(channel,"Only people with + or @ can run this command.");
     // I noticed that sometimes the bot just get out of sync, so ask the sender to retry
     sendRawLine("NAMES #jenkins");
     sendMessage(channel,"I'll refresh the member list, so if you think this is an error, try again in a few seconds.");
 }
 private void help(String channel) {
     sendMessage(channel,"See http://wiki.jenkins-ci.org/display/JENKINS/IRC+Bot");
 }