private String editRolePermission(Operation op, Permission permission, IRole role) {
   IGuild parent = role.getGuild();
   DiscordGuild g =
       cacheService.findGuildById(parent.getID()).orElseGet(() -> new DiscordGuild(parent));
   DiscordRole edit = null;
   for (DiscordRole r : g.getRoles()) {
     if (r.getId().equals(role.getID())) {
       edit = r;
       break;
     }
   }
   if (edit == null) {
     edit = new DiscordRole(role);
     g.getRoles().add(edit);
   }
   if (edit.getName() == null) {
     edit.setName(role.getName());
   }
   changePermission(op, permission, edit);
   g = cacheService.saveGuild(g);
   log.info("Saving new guild/role permission settings: {}", g);
   permissionService.evict();
   return String.format(
       "Modified role %s: %s permission %s",
       edit.getName(), op.name().toLowerCase(), permission.getName());
 }
 private String editPermission(
     Operation op, IMessage message, Permission permission, List<String> args) {
   String first = args.size() >= 1 ? args.get(0) : "";
   String second = args.size() >= 2 ? args.get(1) : "";
   String third = args.size() >= 3 ? args.get(2) : "";
   String fourth = args.size() >= 4 ? args.get(3) : "";
   if (first.equalsIgnoreCase("this")) {
     // this <channel|guild>
     if (second.equalsIgnoreCase("channel")) {
       // this channel
       return editChannelPermission(op, permission, message.getChannel());
     } else if (second.equalsIgnoreCase("server")) {
       // this guild
       if (message.getChannel().isPrivate()) {
         return "Not a valid call from private channels";
       }
       IGuild guild = message.getChannel().getGuild();
       return editGuildPermission(op, permission, guild);
     } else {
       return "Use `this channel` or `this server`";
     }
   } else if (first.equalsIgnoreCase("user")) {
     // user <name or id>
     List<IUser> matching =
         discordService
             .getClient()
             .getGuilds()
             .stream()
             .flatMap(g -> g.getUsers().stream())
             .filter(u -> u.getName().equalsIgnoreCase(second) || u.getID().equals(second))
             .distinct()
             .collect(Collectors.toList());
     if (matching.size() > 1) {
       StringBuilder builder =
           new StringBuilder("Multiple users matched, please narrow search or use ID\n");
       for (IUser user : matching) {
         builder.append(user.getName()).append(" has id `").append(user.getID()).append("`\n");
       }
       return builder.toString();
     } else if (matching.isEmpty()) {
       return "User " + second + " not found in cache";
     } else {
       return editUserPermission(op, permission, matching.get(0));
     }
   } else if (first.equalsIgnoreCase("channel")) {
     // channel <name or id>
     List<IChannel> matching =
         discordService
             .getClient()
             .getGuilds()
             .stream()
             .flatMap(g -> g.getChannels().stream())
             .filter(c -> c.getName().equalsIgnoreCase(second) || c.getID().equals(second))
             .distinct()
             .collect(Collectors.toList());
     if (matching.size() > 1) {
       StringBuilder builder =
           new StringBuilder("Multiple channels matched, please narrow search or use ID\n");
       for (IChannel channel : matching) {
         builder
             .append(channel.getName())
             .append(" has id `")
             .append(channel.getID())
             .append("`\n");
       }
       return builder.toString();
     } else if (matching.isEmpty()) {
       return "Channel " + second + " not found in cache";
     } else {
       return editChannelPermission(op, permission, matching.get(0));
     }
   } else if (first.equalsIgnoreCase("server")) {
     // guild <name or id>
     List<IGuild> matching =
         discordService
             .getClient()
             .getGuilds()
             .stream()
             .filter(g -> g.getName().equalsIgnoreCase(second) || g.getID().equals(second))
             .distinct()
             .collect(Collectors.toList());
     if (matching.size() > 1) {
       StringBuilder builder =
           new StringBuilder("Multiple servers matched, please narrow search or use ID\n");
       for (IGuild guild : matching) {
         builder.append(guild.getName()).append(" has id `").append(guild.getID()).append("`\n");
       }
       return builder.toString();
     } else if (matching.isEmpty()) {
       return "Server " + second + " not found in cache";
     } else {
       return editGuildPermission(op, permission, matching.get(0));
     }
   } else if (first.equalsIgnoreCase("role")) {
     // role <name or id> [in <guild name or id>]
     // when a guild name/id is not specified:
     //      if triggered from a private channel, all case insensitive matches are picked up
     //      if triggered from a public channel, only the channel's guild is searched
     boolean isPrivate = message.getChannel().isPrivate();
     boolean specific = third.equalsIgnoreCase("in") && !fourth.isEmpty();
     List<IRole> matching =
         discordService
             .getClient()
             .getGuilds()
             .stream()
             .filter(
                 g -> {
                   if (isPrivate) {
                     return !specific
                         || g.getName().equalsIgnoreCase(fourth)
                         || g.getID().equals(fourth);
                   } else {
                     if (!specific) {
                       return g.equals(message.getChannel().getGuild());
                     } else {
                       return g.getName().equalsIgnoreCase(fourth) || g.getID().equals(fourth);
                     }
                   }
                 })
             .flatMap(g -> g.getRoles().stream())
             .filter(r -> r.getName().equalsIgnoreCase(second) || r.getID().equals(second))
             .distinct()
             .collect(Collectors.toList());
     if (matching.size() > 1) {
       StringBuilder builder =
           new StringBuilder("Multiple role matched, please narrow search or use ID\n");
       for (IRole role : matching) {
         builder.append(role.getName()).append(" has id `").append(role.getID()).append("`\n");
       }
       return builder.toString();
     } else if (matching.isEmpty()) {
       return "Role " + second + " not found in cache";
     } else {
       return editRolePermission(op, permission, matching.get(0));
     }
   } else {
     return "Invalid argument format! Check details with `.perm -?`";
   }
 }