channelSet;
+ private String helpCommandGroup;
+
+ public IrcServiceImplementor(GeneratedBeanGizmoAdaptor classOutput, Class> ircServiceClass) {
+ this.ircServiceClass = ircServiceClass;
+ this.classOutput = classOutput;
+ channelSet = new HashSet<>();
+ }
+
+ public String generateImplementation() {
+ final String generatedClassName = ircServiceClass.getName() + "$VileBotImplementation";
+ if (ircServiceClass.getAnnotation(ApplicationScoped.class) == null) {
+ throw new IllegalStateException("IRC Service class (" + ircServiceClass + ") is not @ApplicationScoped. Maybe add @ApplicationScoped?");
+ }
+
+ classCreator = ClassCreator.builder()
+ .className(ircServiceClass.getName() + "$VileBotImplementation")
+ .classOutput(classOutput)
+ .superClass(IRCService.class)
+ .build();
+
+ classCreator.addAnnotation(ApplicationScoped.class);
+
+ ircServiceField = FieldDescriptor.of(classCreator.getClassName(), "ircService", ircServiceClass);
+ helpServiceField = FieldDescriptor.of(classCreator.getClassName(), "helpService", HelpService.class);
+ classCreator.getFieldCreator(ircServiceField)
+ .setModifiers(Modifier.PUBLIC)
+ .addAnnotation(Inject.class);
+
+ classCreator.getFieldCreator(helpServiceField)
+ .setModifiers(Modifier.PUBLIC)
+ .addAnnotation(Inject.class);
+
+
+ helpCommandGroup = ircServiceClass.getSimpleName();
+ if (helpCommandGroup.endsWith("Service")) {
+ helpCommandGroup = helpCommandGroup.substring(0, helpCommandGroup.length() - "Service".length());
+ }
+
+ if (ircServiceClass.isAnnotationPresent(Bot.class)) {
+ String botName = ircServiceClass.getAnnotation(Bot.class).value();
+ MethodCreator botNickMethodCreator = classCreator.getMethodCreator(MethodDescriptor.ofMethod(classCreator.getClassName(), "botNick", String.class));
+ botNickMethodCreator.returnValue(botNickMethodCreator.load(botName));
+ }
+
+ postConstructMethodCreator = classCreator.getMethodCreator(MethodDescriptor.ofMethod(classCreator.getClassName(), "__postConstruct", void.class));
+ postConstructMethodCreator.addAnnotation(PostConstruct.class);
+ for (Method method : ircServiceClass.getDeclaredMethods()) {
+ if (hasIrcServiceAnnotation(method) && !Modifier.isPublic(method.getModifiers())) {
+ throw new IllegalStateException("IRC Service annotation detected on non-public method (" + method.toGenericString() + "). Maybe make the method public?" );
+ }
+ Optional.ofNullable(method.getAnnotation(OnChannelMessage.class)).ifPresent(annotation -> implementOnChannelMessage(method, annotation, true));
+ Optional.ofNullable(method.getAnnotation(OnPrivateMessage.class)).ifPresent(annotation -> implementOnPrivateMessage(method, annotation, true));
+ Optional.ofNullable(method.getAnnotation(OnMessage.class)).ifPresent(annotation -> implementOnMessage(method, annotation));
+ Optional.ofNullable(method.getAnnotation(Handler.class)).ifPresent(annotation -> implementHandler(method, annotation));
+ }
+
+ methodCreator = classCreator.getMethodCreator(MethodDescriptor.ofMethod(IRCService.class, "getChannelsToJoin",
+ Collection.class));
+ ResultHandle outputArray = methodCreator.newInstance(MethodDescriptor.ofConstructor(ArrayList.class, int.class),
+ methodCreator.load(channelSet.size()));
+ channelSet.forEach(channel -> {
+ ResultHandle channelResultHandle = methodCreator.load(channel);
+ ResultHandle actualChannel = methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(IRCService.class, "getChannel", Collection.class, String.class),
+ methodCreator.getThis(), channelResultHandle);
+ methodCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(Collection.class, "addAll", boolean.class, Collection.class),
+ outputArray, actualChannel);
+ });
+ methodCreator.returnValue(outputArray);
+
+ postConstructMethodCreator.returnValue(null);
+ classCreator.close();
+ return generatedClassName;
+ }
+
+ private boolean hasIrcServiceAnnotation(Method method) {
+ return method.isAnnotationPresent(OnChannelMessage.class)
+ || method.isAnnotationPresent(OnPrivateMessage.class)
+ || method.isAnnotationPresent(OnMessage.class)
+ || method.isAnnotationPresent(Handler.class);
+ }
+
+ /**
+ * Generates code that looks like this:
+ *
+ *
+ * @Handler
+ * public void method$Handler(PrivateMessageEvent privateMessage) {
+ * if (!privateMessage.isToClient())) {
+ * return;
+ * }
+ * Pattern pattern = Pattern.compile(...);
+ * Matcher matcher = pattern.matcher(privateMessage.getMessage());
+ * if (!matcher.matches()) {
+ * return;
+ * }
+ * Integer intArg = Integer.parseInt(matcher.group("intArg"));
+ * String stringArg = matcher.group("stringArg");
+ * return service.theMethod(intArg, stringArg);
+ * }
+ *
+ * @param method
+ * @param onMessage
+ */
+ private void implementOnMessage(Method method, OnMessage onMessage) {
+ implementOnPrivateMessage(method, new OnPrivateMessage() {
+
+ @Override public Class extends Annotation> annotationType() {
+ return OnPrivateMessage.class;
+ }
+
+ @Override public String value() {
+ return onMessage.value();
+ }
+ }, true);
+
+ implementOnChannelMessage(method, new OnChannelMessage() {
+
+ @Override public Class extends Annotation> annotationType() {
+ return OnChannelMessage.class;
+ }
+
+ @Override public String value() {
+ return onMessage.value();
+ }
+
+ @Override public String channel() {
+ return "<>";
+ }
+ }, false);
+ }
+
+ /**
+ * Generates code that looks like this:
+ *
+ *
+ * @Handler
+ * public void method$Handler(PrivateMessageEvent privateMessage) {
+ * if (!privateMessage.isToClient())) {
+ * return;
+ * }
+ * Pattern pattern = Pattern.compile(...);
+ * Matcher matcher = pattern.matcher(privateMessage.getMessage());
+ * if (!matcher.matches()) {
+ * return;
+ * }
+ * Integer intArg = Integer.parseInt(matcher.group("intArg"));
+ * String stringArg = matcher.group("stringArg");
+ * return service.theMethod(intArg, stringArg);
+ * }
+ *
+ * @param method
+ * @param onPrivateMessage
+ */
+ private void implementOnPrivateMessage(Method method, OnPrivateMessage onPrivateMessage, boolean createHelp) {
+ methodCreator = classCreator.getMethodCreator(getSafeMethodSignature(method) + "$$OnPrivateMessage", void.class, PrivateMessageEvent.class);
+ AnnotationCreator annotationCreator = methodCreator.addAnnotation(Handler.class);
+ annotationCreator.addValue("delivery", Invoke.Asynchronously);
+
+ ResultHandle privateMessageEventResultHandle = methodCreator.getMethodParam(0);
+
+ BytecodeCreator matcherBytecodeCreator = methodCreator;
+ ResultHandle ircMessageTextResultHandle = matcherBytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(PrivateMessageEvent.class, "getMessage", String.class),
+ privateMessageEventResultHandle);
+
+ ImplementedQuery implementedQuery = implementQuery(method, matcherBytecodeCreator, onPrivateMessage.value(), createHelp, ircMessageTextResultHandle,
+ (bytecode, parameterType) -> {
+ if (parameterType.isAssignableFrom(PrivateMessageEvent.class)) {
+ return Optional.of(privateMessageEventResultHandle);
+ } else if (parameterType.isAssignableFrom(Client.class)) {
+ return Optional.of(bytecode.invokeVirtualMethod(MethodDescriptor.ofMethod(ClientLinked.class, "getClient", Client.class),
+ privateMessageEventResultHandle));
+ } else if (parameterType.isAssignableFrom(User.class)) {
+ return Optional.of(bytecode.invokeInterfaceMethod(MethodDescriptor.ofMethod(ActorEvent.class, "getActor", Actor.class),
+ privateMessageEventResultHandle));
+ }
+ return Optional.empty();
+ });
+ ResultHandle[] methodArgumentResultHandles = implementedQuery.methodArgumentResultHandles;
+ BytecodeCreator processorBytecode = implementedQuery.processorBytecode;
+
+ ResultHandle ircServiceResultHandle = processorBytecode.readInstanceField(ircServiceField, methodCreator.getThis());
+ sendResponse(method, processorBytecode, ircServiceResultHandle, methodArgumentResultHandles, (responseByteCodeCreator, textResultHandle) -> {
+ responseByteCodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(PrivateMessageEvent.class, "sendReply", void.class, String.class),
+ privateMessageEventResultHandle, textResultHandle);
+ });
+ processorBytecode.returnValue(null);
+ }
+
+ /**
+ * Generates code that looks like this:
+ *
+ *
+ * @Handler
+ * public void method$Handler(ChannelMessage channelMessage) {
+ * if (!targetChannel.equals(channelMessage.getChannel().getMessagingName())) {
+ * return;
+ * }
+ * Pattern pattern = Pattern.compile(...);
+ * Matcher matcher = pattern.matcher(channelMessage.getMessage());
+ * if (!matcher.matches()) {
+ * return;
+ * }
+ * Integer intArg = Integer.parseInt(matcher.group("intArg"));
+ * String stringArg = matcher.group("stringArg");
+ * return service.theMethod(intArg, stringArg);
+ * }
+ *
+ * @param method
+ * @param onChannelMessage
+ */
+ private void implementOnChannelMessage(Method method, OnChannelMessage onChannelMessage, boolean createHelp) {
+ methodCreator = classCreator.getMethodCreator(getSafeMethodSignature(method) + "$$OnChannelMessage", void.class, ChannelMessageEvent.class);
+ AnnotationCreator annotationCreator = methodCreator.addAnnotation(Handler.class);
+ annotationCreator.addValue("delivery", Invoke.Asynchronously);
+
+ ResultHandle isChannelTarget;
+ ResultHandle channelMessageEventResultHandle = methodCreator.getMethodParam(0);
+ if (!onChannelMessage.channel().equals("<>")) {
+ channelSet.add(onChannelMessage.channel());
+ ResultHandle channelResultHandle = methodCreator.load(onChannelMessage.channel());
+ ResultHandle actualChannelToCheckResultHandle = methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(IRCService.class, "getChannel", Collection.class, String.class),
+ methodCreator.getThis(), channelResultHandle);
+
+ ResultHandle channelInstanceResultHandle = methodCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(ChannelEvent.class,
+ "getChannel", Channel.class), channelMessageEventResultHandle);
+ ResultHandle channelMessagingResultHandle = methodCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(Channel.class,
+ "getMessagingName", String.class), channelInstanceResultHandle);
+ isChannelTarget = methodCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(Collection.class, "contains",
+ boolean.class, Object.class), actualChannelToCheckResultHandle, channelMessagingResultHandle);
+ } else {
+ isChannelTarget = methodCreator.load(true);
+ }
+
+ BranchResult channelMatchesBranchResult = methodCreator.ifFalse(isChannelTarget);
+ channelMatchesBranchResult.trueBranch().returnValue(null);
+ BytecodeCreator matcherBytecodeCreator = channelMatchesBranchResult.falseBranch();
+ ResultHandle ircMessageTextResultHandle = matcherBytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(ChannelMessageEvent.class, "getMessage", String.class),
+ channelMessageEventResultHandle);
+
+ ImplementedQuery implementedQuery = implementQuery(method, matcherBytecodeCreator, onChannelMessage.value(), createHelp, ircMessageTextResultHandle,
+ (bytecode, parameterType) -> {
+ if (parameterType.isAssignableFrom(ChannelMessageEvent.class)) {
+ return Optional.of(channelMessageEventResultHandle);
+ } else if (parameterType.isAssignableFrom(Client.class)) {
+ return Optional.of(bytecode.invokeInterfaceMethod(MethodDescriptor.ofMethod(ClientLinked.class, "getClient", Client.class),
+ channelMessageEventResultHandle));
+ } else if (parameterType.isAssignableFrom(User.class)) {
+ return Optional.of(bytecode.invokeInterfaceMethod(MethodDescriptor.ofMethod(ActorEvent.class, "getActor", Actor.class),
+ channelMessageEventResultHandle));
+ } else if (parameterType.isAssignableFrom(Channel.class)) {
+ return Optional.of(bytecode.invokeInterfaceMethod(MethodDescriptor.ofMethod(ChannelEvent.class,
+ "getChannel", Channel.class),
+ channelMessageEventResultHandle));
+ }
+ return Optional.empty();
+ });
+ ResultHandle[] methodArgumentResultHandles = implementedQuery.methodArgumentResultHandles;
+ BytecodeCreator processorBytecode = implementedQuery.processorBytecode;
+
+ ResultHandle ircServiceResultHandle = processorBytecode.readInstanceField(ircServiceField, methodCreator.getThis());
+ sendResponse(method, processorBytecode, ircServiceResultHandle, methodArgumentResultHandles, (responseByteCodeCreator, textResultHandle) -> {
+ responseByteCodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(ChannelMessageEvent.class, "sendReply", void.class, String.class),
+ channelMessageEventResultHandle, textResultHandle);
+ });
+ processorBytecode.returnValue(null);
+ }
+
+ private void implementHandler(Method method, Handler handler) {
+ if (method.getParameterCount() != 1) {
+ throw new IllegalStateException("Handler methods can only take 1 argument!");
+ }
+ Class> eventClass = method.getParameterTypes()[0];
+ methodCreator = classCreator.getMethodCreator(getSafeMethodSignature(method) + eventClass.getSimpleName() + "Handler", void.class, eventClass);
+ AnnotationCreator annotationCreator = methodCreator.addAnnotation(Handler.class);
+ annotationCreator.addValue("delivery", handler.delivery());
+ ResultHandle eventResultHandle = methodCreator.getMethodParam(0);
+ ResultHandle ircServiceResultHandle = methodCreator.readInstanceField(ircServiceField, methodCreator.getThis());
+
+ sendResponse(method, methodCreator, ircServiceResultHandle, new ResultHandle[]{ eventResultHandle }, (responseByteCodeCreator, textResultHandle) -> {
+ if (ChannelEvent.class.isAssignableFrom(eventClass)) {
+ ResultHandle channelResultHandle = responseByteCodeCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(ChannelEvent.class, "getChannel", Channel.class),
+ eventResultHandle);
+ responseByteCodeCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(Channel.class, "sendMessage", void.class, String.class),
+ channelResultHandle, textResultHandle);
+ } else if (ActorEvent.class.isAssignableFrom(eventClass)) {
+ ResultHandle actorResultHandle = responseByteCodeCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(ActorEvent.class, "getActor", Actor.class),
+ eventResultHandle);
+ ResultHandle client = responseByteCodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(ClientLinked.class, "getClient", Client.class),
+ eventResultHandle);
+ ResultHandle target = responseByteCodeCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(Actor.class, "getName", String.class),
+ actorResultHandle);
+ responseByteCodeCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(Client.class, "sendMessage", void.class, String.class, String.class),
+ client, target, textResultHandle);
+ } else {
+ throw new IllegalStateException("Cannot generate a reply since it not a ChannelEvent or an ActorEvent; use a void return type to observe this event");
+ }
+ });
+ methodCreator.returnValue(null);
+ }
+
+ private ImplementedQuery implementQuery(Method method, BytecodeCreator matcherBytecodeCreator, String pattern, boolean createHelp, ResultHandle ircMessageTextResultHandle, BiFunction> additionalMatcherBiFunction) {
+
+ FieldDescriptor patternField = classCreator.getFieldCreator(FieldDescriptor.of(classCreator.getClassName(), getSafeMethodSignature(method) + "$pattern", Pattern.class))
+ .getFieldDescriptor();
+ postConstructMethodCreator.writeInstanceField(patternField, postConstructMethodCreator.getThis(),
+ postConstructMethodCreator.invokeStaticMethod(MethodDescriptor.ofMethod(Pattern.class, "compile", Pattern.class, String.class),
+ postConstructMethodCreator.load(getPatternRegex(method, pattern))));
+
+
+ if (createHelp && !method.isAnnotationPresent(NoHelp.class)) {
+ postConstructMethodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(HelpService.class, "addHelpCommand", void.class, String.class, String.class),
+ postConstructMethodCreator.readInstanceField(helpServiceField, postConstructMethodCreator.getThis()),
+ postConstructMethodCreator.load(helpCommandGroup),
+ postConstructMethodCreator.load(getHelpString(method, pattern)));
+ }
+ ResultHandle patternMatcherResultHandle = matcherBytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(Pattern.class, "matcher", Matcher.class, CharSequence.class),
+ matcherBytecodeCreator.readInstanceField(patternField, matcherBytecodeCreator.getThis()),
+ matcherBytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(String.class, "stripTrailing", String.class), ircMessageTextResultHandle));
+ ResultHandle matchesResultHandle = matcherBytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(Matcher.class, "matches", boolean.class),
+ patternMatcherResultHandle);
+ BranchResult branchResult = matcherBytecodeCreator.ifFalse(matchesResultHandle);
+ branchResult.trueBranch().returnValue(null);
+ BytecodeCreator processorBytecode = branchResult.falseBranch();
+
+
+ ResultHandle[] methodArgumentResultHandles = new ResultHandle[method.getParameterCount()];
+ for (int i = 0; i < method.getParameterCount(); i++) {
+ Parameter parameter = method.getParameters()[i];
+ Optional maybeResult = additionalMatcherBiFunction.apply(processorBytecode, parameter.getType());
+ methodArgumentResultHandles[i] = maybeResult
+ .orElseGet(() -> extractParameterFromMatcher(processorBytecode, parameter, patternMatcherResultHandle));
+ }
+ return new ImplementedQuery(methodArgumentResultHandles, processorBytecode);
+ }
+
+ private void sendResponse(Method method, BytecodeCreator processorBytecode, ResultHandle ircServiceResultHandle, ResultHandle[] methodArgumentResultHandles, BiConsumer responseSender) {
+ ResultHandle methodResult = processorBytecode.invokeVirtualMethod(MethodDescriptor.ofMethod(method),
+ ircServiceResultHandle, methodArgumentResultHandles);
+ ResultHandle stringReturnValue = getReturnValue(processorBytecode, method, methodResult);
+
+ if (!method.getReturnType().isAssignableFrom(void.class)) {
+ BytecodeCreator replyBytecodeCreator = processorBytecode.ifNotNull(stringReturnValue).trueBranch();
+ ResultHandle splitResultHandle = replyBytecodeCreator.invokeStaticMethod(MethodDescriptor.ofMethod(IRCService.class, "splitIrcMessage", String[].class,
+ String.class), stringReturnValue);
+ AssignableResultHandle indexResultHandle = replyBytecodeCreator.createVariable(int.class);
+ replyBytecodeCreator.assign(indexResultHandle, replyBytecodeCreator.load(0));
+ BytecodeCreator replyLoopBytecodeCreator = replyBytecodeCreator.whileLoop(conditionCreator ->
+ conditionCreator.ifIntegerLessThan(indexResultHandle, conditionCreator.arrayLength(splitResultHandle))
+ ).block();
+ responseSender.accept(replyLoopBytecodeCreator, replyLoopBytecodeCreator.readArrayValue(splitResultHandle, indexResultHandle));
+ replyLoopBytecodeCreator.assign(indexResultHandle, replyLoopBytecodeCreator.increment(indexResultHandle));
+ }
+ }
+
+ private static final class ImplementedQuery {
+ final ResultHandle[] methodArgumentResultHandles;
+ final BytecodeCreator processorBytecode;
+
+ public ImplementedQuery(ResultHandle[] methodArgumentResultHandles, BytecodeCreator processorBytecode) {
+ this.methodArgumentResultHandles = methodArgumentResultHandles;
+ this.processorBytecode = processorBytecode;
+ }
+ }
+
+ private String getSafeMethodSignature(Method method) {
+ return method.getName() + "$$" + Arrays.stream(method.getParameters()).map(parameter -> parameter.getType().getName().replace('.', '$'))
+ .collect(Collectors.joining("$$")) + "$$Handler";
+ }
+
+ private ResultHandle extractParameterFromMatcher(BytecodeCreator bytecodeCreator, Parameter parameter, ResultHandle matcherResultHandle) {
+ ResultHandle parameterNameResultHandle = bytecodeCreator.load(parameter.getName());
+ ResultHandle matcherText = bytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(Matcher.class, "group", String.class, String.class),
+ matcherResultHandle, parameterNameResultHandle);
+
+ AssignableResultHandle returnValue = bytecodeCreator.createVariable(parameter.getType());
+ BranchResult ifNullBranchResult = bytecodeCreator.ifNull(matcherText);
+ bytecodeCreator = ifNullBranchResult.trueBranch();
+ if (parameter.getParameterizedType() instanceof ParameterizedType && Optional.class.isAssignableFrom((Class>) ((ParameterizedType) parameter.getParameterizedType()).getRawType())) {
+ bytecodeCreator.assign(returnValue, bytecodeCreator.invokeStaticMethod(MethodDescriptor.ofMethod(Optional.class, "empty", Optional.class)));
+ } else {
+ bytecodeCreator.assign(returnValue, bytecodeCreator.loadNull());
+ }
+
+ bytecodeCreator = ifNullBranchResult.falseBranch();
+
+ if (parameter.getParameterizedType() instanceof Class) {
+ MethodDescriptor valueParser = getValueOfForType(parameter.getType());
+ bytecodeCreator.assign(returnValue, bytecodeCreator.invokeStaticMethod(valueParser, matcherText));
+ } else if (parameter.getParameterizedType() instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) parameter.getParameterizedType();
+ if (List.class.isAssignableFrom((Class>) parameterizedType.getRawType())) {
+ ResultHandle listDelimiterResultHandle = bytecodeCreator.load(getListDelimiter(parameter));
+ ResultHandle itemsArrayResultHandle = bytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(String.class, "split", String[].class, String.class),
+ matcherText, listDelimiterResultHandle);
+ Class> arrayType = (Class>) parameterizedType.getActualTypeArguments()[0];
+ MethodDescriptor valueParser = getValueOfForType(arrayType);
+ ResultHandle arrayLengthResultHandle = bytecodeCreator.arrayLength(itemsArrayResultHandle);
+ ResultHandle parsedValueListResultHandle = bytecodeCreator.newInstance(MethodDescriptor.ofConstructor(ArrayList.class, int.class),
+ arrayLengthResultHandle);
+ AssignableResultHandle indexResultHandle = bytecodeCreator.createVariable(int.class);
+ bytecodeCreator.assign(indexResultHandle, bytecodeCreator.load(0));
+ WhileLoop whileLoop = bytecodeCreator.whileLoop(conditionFunction -> conditionFunction.ifIntegerLessThan(indexResultHandle, arrayLengthResultHandle));
+
+ BytecodeCreator whileLoopBlock = whileLoop.block();
+ ResultHandle parsedValueResultHandle = whileLoopBlock.invokeStaticMethod(valueParser, whileLoopBlock.readArrayValue(itemsArrayResultHandle, indexResultHandle));
+
+ whileLoopBlock.invokeInterfaceMethod(MethodDescriptor.ofMethod(List.class, "add", boolean.class, Object.class),
+ parsedValueListResultHandle, parsedValueResultHandle);
+ whileLoopBlock.assign(indexResultHandle, whileLoopBlock.increment(indexResultHandle));
+
+ bytecodeCreator.assign(returnValue, parsedValueListResultHandle);
+ } else if (Optional.class.isAssignableFrom((Class>) parameterizedType.getRawType())) {
+ BranchResult isEmptyBranchResult = bytecodeCreator.ifTrue(bytecodeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(String.class, "isEmpty", boolean.class),
+ matcherText));
+ bytecodeCreator = isEmptyBranchResult.trueBranch();
+ bytecodeCreator.assign(returnValue, bytecodeCreator.invokeStaticMethod(MethodDescriptor.ofMethod(Optional.class, "empty", Optional.class)));
+ bytecodeCreator = isEmptyBranchResult.falseBranch();
+ Class> optionalType = (Class>) parameterizedType.getActualTypeArguments()[0];
+ MethodDescriptor valueParser = getValueOfForType(optionalType);
+ bytecodeCreator.assign(returnValue, bytecodeCreator.invokeStaticMethod(MethodDescriptor.ofMethod(Optional.class, "of",
+ Optional.class, Object.class), bytecodeCreator.invokeStaticMethod(valueParser, matcherText)));
+ }
+ } else {
+ throw new IllegalStateException("Illegal type (" + parameter.getType() + ") for parameter (" + parameter.getName() + ").");
+ }
+ return returnValue;
+ }
+
+ private MethodDescriptor getValueOfForType(Class> type) {
+ if (type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)) {
+ return MethodDescriptor.ofMethod(Integer.class, "parseInt", int.class, String.class);
+ } else if (type.isAssignableFrom(long.class) || type.isAssignableFrom(Long.class)) {
+ return MethodDescriptor.ofMethod(Long.class, "parseLong", long.class, String.class);
+ } else if (type.isAssignableFrom(String.class)) {
+ // pointless, but makes generated code more readable
+ return MethodDescriptor.ofMethod(String.class, "valueOf", String.class, Object.class);
+ } else if (type.isAssignableFrom(Nick.class)) {
+ return MethodDescriptor.ofMethod(Nick.class, "valueOf", Nick.class, String.class);
+ }else if (type.isEnum()) {
+ return MethodDescriptor.ofMethod(type, "valueOf", type, String.class);
+ } else {
+ throw new IllegalArgumentException("Invalid type (" + type + ").");
+ }
+ }
+
+ private String getPatternRegex(Method method, String parameterPatternString) {
+ String splitRegex = "@([a-zA-Z0-9$_]+)";
+ // add a space to the beginning to guarantee first token is not
+ // a parameter
+ String patternString = (" " + parameterPatternString);
+ String[] patternStringParts = patternString.split(splitRegex);
+ String remaining = patternString;
+
+ StringBuilder patternRegexBuilder = new StringBuilder("^");
+ for (String part : patternStringParts) {
+ patternRegexBuilder.append(part);
+ remaining = remaining.substring(part.length());
+ if (!remaining.isEmpty()) {
+ int endIndex = 1;
+ while (endIndex < remaining.length() && Character.isJavaIdentifierPart(remaining.charAt(endIndex))) {
+ endIndex++;
+ }
+ String variableName = remaining.substring(1, endIndex);
+ remaining = remaining.substring(variableName.length() + 1);
+
+ Parameter methodParameter = Arrays.stream(method.getParameters()).filter(parameter -> parameter.getName().equals(variableName))
+ .findFirst().orElseThrow(() -> new IllegalArgumentException("Expected a parameter with name (" +
+ variableName + ") on method (" + method.getName()
+ + ") for pattern (" + patternString + "), but no such parameter exists." ));
+
+ patternRegexBuilder.append("(?<" + variableName + ">" +
+ getRegexForType(methodParameter, methodParameter.getParameterizedType())
+ + ")");
+ }
+ }
+ patternRegexBuilder.append("$");
+ // remove the additional space we added
+ patternRegexBuilder.deleteCharAt(1);
+ return patternRegexBuilder.toString();
+ }
+
+ private String getHelpString(Method method, String parameterPatternString) {
+ String splitRegex = "@([a-zA-Z0-9$_]+)";
+ // add a space to the beginning to guarantee first token is not
+ // a parameter
+ String helpString = (" " + parameterPatternString);
+ String[] helpStringParts = helpString.split(splitRegex);
+ String remaining = helpString;
+
+ StringBuilder helpBuilder = new StringBuilder();
+ for (String part : helpStringParts) {
+ helpBuilder.append(part);
+ remaining = remaining.substring(part.length());
+ if (!remaining.isEmpty()) {
+ int endIndex = 1;
+ while (endIndex < remaining.length() && Character.isJavaIdentifierPart(remaining.charAt(endIndex))) {
+ endIndex++;
+ }
+ String variableName = remaining.substring(1, endIndex);
+ remaining = remaining.substring(variableName.length() + 1);
+
+ Parameter methodParameter = Arrays.stream(method.getParameters()).filter(parameter -> parameter.getName().equals(variableName))
+ .findFirst().orElseThrow(() -> new IllegalArgumentException("Expected a parameter with name (" +
+ variableName + ") on method (" + method.getName()
+ + ") for pattern (" + helpString + "), but no such parameter exists." ));
+
+ helpBuilder.append(getHelpForType(methodParameter, methodParameter.getParameterizedType()));
+ }
+ }
+
+ // remove the additional space we added
+ helpBuilder.deleteCharAt(0);
+ return "{ " + helpBuilder + " }";
+ }
+
+ private String getListDelimiter(Parameter parameter) {
+ if (parameter.isAnnotationPresent(Delimiter.class)) {
+ return parameter.getAnnotation(Delimiter.class).value();
+ } else {
+ return "\\s";
+ }
+ }
+
+ private String getRegexForType(Parameter parameter, Type type) {
+ if (type instanceof Class) {
+ Class> parameterType = (Class>) type;
+ if (int.class.isAssignableFrom(parameterType) ||
+ Integer.class.isAssignableFrom(parameterType) ||
+ long.class.isAssignableFrom(parameterType) ||
+ Long.class.isAssignableFrom(parameterType)
+ ) {
+ return "-?\\d+";
+ } else if (String.class.isAssignableFrom(parameterType)) {
+ if (parameter.isAnnotationPresent(Regex.class)) {
+ return parameter.getAnnotation(Regex.class).value();
+ } else {
+ return ".+";
+ }
+ } else if (Nick.class.isAssignableFrom(parameterType)) {
+ return "(?:x|)(?:[a-zA-Z0-9]+)(?:\\p{Punct}+[a-zA-Z0-9]*|)";
+ } else if (parameterType.isEnum()) {
+ return Arrays.stream(parameterType.getEnumConstants())
+ .map(Object::toString).collect(Collectors.joining("|"));
+ } else {
+ throw new IllegalArgumentException("Illegal type (" + type + ") on parameter (" + parameter + ")");
+ }
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ Class> rawType = (Class>) parameterizedType.getRawType();
+ if (List.class.isAssignableFrom(rawType)) {
+ String innerTypeRegex = "(?:" + getRegexForType(parameter, parameterizedType.getActualTypeArguments()[0]) + ")";
+ String collectionDelimiterRegex = getListDelimiter(parameter);
+ return innerTypeRegex + "(?:(?:" + collectionDelimiterRegex +")" + innerTypeRegex + ")*";
+ } else if (Optional.class.isAssignableFrom(rawType)) {
+ return "(?:" + getRegexForType(parameter, parameterizedType.getActualTypeArguments()[0]) + ")?";
+ }else {
+ throw new IllegalArgumentException("Illegal type (" + type + ") on parameter (" + parameter + ")");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid type (" + type + ").");
+ }
+ }
+
+ private String getHelpForType(Parameter parameter, Type type) {
+ if (type instanceof Class) {
+ Class> parameterType = (Class>) type;
+ if (int.class.isAssignableFrom(parameterType) ||
+ Integer.class.isAssignableFrom(parameterType) ||
+ long.class.isAssignableFrom(parameterType) ||
+ Long.class.isAssignableFrom(parameterType)
+ ) {
+ return "<" + parameter.getName() + ":number>";
+ } else if (String.class.isAssignableFrom(parameterType)) {
+ if (parameter.isAnnotationPresent(Regex.class)) {
+ return "<" + parameter.getName() + ":" + parameter.getAnnotation(Regex.class).value() + ">";
+ } else {
+ return "<" + parameter.getName() + ">";
+ }
+ } else if (Nick.class.isAssignableFrom(parameterType)) {
+ return "<" + parameter.getName() + ":nick>";
+ } else if (parameterType.isEnum()) {
+ return "<" + parameter.getName() + ":" + Arrays.stream(parameterType.getEnumConstants())
+ .map(Object::toString).collect(Collectors.joining("|")) + ">";
+ } else {
+ throw new IllegalArgumentException("Illegal type (" + type + ") on parameter (" + parameter + ")");
+ }
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ Class> rawType = (Class>) parameterizedType.getRawType();
+ if (List.class.isAssignableFrom(rawType)) {
+ String innerHelp = getHelpForType(parameter, parameterizedType.getActualTypeArguments()[0]);
+ return innerHelp + "[" + getListDelimiter(parameter) + "<" + parameter.getName() + "2>...]";
+ } else if (Optional.class.isAssignableFrom(rawType)) {
+ return "[" + getHelpForType(parameter, parameterizedType.getActualTypeArguments()[0]) + "]";
+ }else {
+ throw new IllegalArgumentException("Illegal type (" + type + ") on parameter (" + parameter + ")");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid type (" + type + ").");
+ }
+ }
+
+ private ResultHandle getReturnValue(BytecodeCreator bytecodeCreator, Method method, ResultHandle methodReturnValue) {
+ if (method.getReturnType().isAssignableFrom(void.class)) {
+ return null;
+ } else if (method.getReturnType().isAssignableFrom(String.class)) {
+ return methodReturnValue;
+ } else {
+ return bytecodeCreator.invokeStaticMethod(MethodDescriptor.ofMethod(String.class, "valueOf", String.class, Object.class), methodReturnValue);
+ }
+ }
+}
diff --git a/quarkus-irc-bot/deployment/src/main/java/com/oldterns/irc/bot/deployment/IrcServiceProcessor.java b/quarkus-irc-bot/deployment/src/main/java/com/oldterns/irc/bot/deployment/IrcServiceProcessor.java
new file mode 100644
index 00000000..33254b53
--- /dev/null
+++ b/quarkus-irc-bot/deployment/src/main/java/com/oldterns/irc/bot/deployment/IrcServiceProcessor.java
@@ -0,0 +1,75 @@
+package com.oldterns.irc.bot.deployment;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import com.oldterns.irc.bot.annotations.OnChannelMessage;
+import com.oldterns.irc.bot.annotations.OnMessage;
+import com.oldterns.irc.bot.annotations.OnPrivateMessage;
+import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
+import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
+import io.quarkus.deployment.builditem.FeatureBuildItem;
+import net.engio.mbassy.listener.Handler;
+import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.IndexView;
+
+class IrcServiceProcessor {
+
+ private static final String FEATURE = "irc-bot";
+
+ @BuildStep
+ FeatureBuildItem feature() {
+ return new FeatureBuildItem(FEATURE);
+ }
+
+ @BuildStep
+ void createIrcServiceImplementations(
+ CombinedIndexBuildItem combinedIndex,
+ BuildProducer generatedBeanConsumer) {
+ DotName onChannelMessageDotName = DotName.createSimple(OnChannelMessage.class.getName());
+ DotName onPrivateMessageDotName = DotName.createSimple(OnPrivateMessage.class.getName());
+ DotName onMessageDotName = DotName.createSimple(OnMessage.class.getName());
+ DotName handlerDotName = DotName.createSimple(Handler.class.getName());
+
+ IndexView indexView = combinedIndex.getIndex();
+ GeneratedBeanGizmoAdaptor classOutput = new GeneratedBeanGizmoAdaptor(generatedBeanConsumer);
+
+ Collection classWithAnnotations = new HashSet<>();
+
+ for (AnnotationInstance annotationInstance : indexView
+ .getAnnotations(onChannelMessageDotName)) {
+ classWithAnnotations.add(annotationInstance.target().asMethod().declaringClass());
+ }
+
+ for (AnnotationInstance annotationInstance : indexView
+ .getAnnotations(onPrivateMessageDotName)) {
+ classWithAnnotations.add(annotationInstance.target().asMethod().declaringClass());
+ }
+
+ for (AnnotationInstance annotationInstance : indexView
+ .getAnnotations(onMessageDotName)) {
+ classWithAnnotations.add(annotationInstance.target().asMethod().declaringClass());
+ }
+
+ for (AnnotationInstance annotationInstance : indexView
+ .getAnnotations(handlerDotName)) {
+ classWithAnnotations.add(annotationInstance.target().asMethod().declaringClass());
+ }
+
+ for (ClassInfo classInfo : classWithAnnotations) {
+ try {
+ Class> ircServiceClass = Class.forName(classInfo.name().toString(), false, Thread.currentThread().getContextClassLoader());
+ IrcServiceImplementor implementor = new IrcServiceImplementor(classOutput, ircServiceClass);
+ implementor.generateImplementation();
+
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Unable to find class (" + classInfo.name() + ").", e);
+ }
+ }
+ }
+}
diff --git a/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/ExampleService.java b/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/ExampleService.java
new file mode 100644
index 00000000..908ad3c9
--- /dev/null
+++ b/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/ExampleService.java
@@ -0,0 +1,103 @@
+package com.oldterns.irc.bot.test;
+
+import com.oldterns.irc.bot.Nick;
+import com.oldterns.irc.bot.annotations.Delimiter;
+import com.oldterns.irc.bot.annotations.NoHelp;
+import com.oldterns.irc.bot.annotations.OnChannelMessage;
+import org.kitteh.irc.client.library.Client;
+import org.kitteh.irc.client.library.element.Channel;
+import org.kitteh.irc.client.library.element.User;
+import org.kitteh.irc.client.library.event.channel.ChannelMessageEvent;
+
+import javax.enterprise.context.ApplicationScoped;
+import java.util.List;
+import java.util.Optional;
+
+@ApplicationScoped
+public class ExampleService {
+
+ static ExampleService INSTANCE;
+ ExampleService mock;
+
+ public ExampleService() {
+ INSTANCE = this;
+ }
+
+ public void setMock(ExampleService mock) {
+ this.mock = mock;
+ }
+
+ @OnChannelMessage("!noargs")
+ public void noArgs() {
+ mock.noArgs();
+ }
+
+ // Test classes lose their parameter names :(
+ @OnChannelMessage("!string @arg0")
+ public void stringArg(String arg0) {
+ mock.stringArg(arg0);
+ }
+
+ @OnChannelMessage("!int @arg0")
+ public void intArg(Integer arg0) {
+ mock.intArg(arg0);
+ }
+
+ @OnChannelMessage("!maybe ?@arg0")
+ public void optionalIntArg(Optional arg0) {
+ mock.optionalIntArg(arg0);
+ }
+
+ @OnChannelMessage("!list @arg0")
+ public void listArg(@Delimiter(",") List arg0) {
+ mock.listArg(arg0);
+ }
+
+ @OnChannelMessage("!client")
+ public void clientArg(Client client) {
+ mock.clientArg(client);
+ }
+
+ @OnChannelMessage("!user")
+ public void userArg(User user) {
+ mock.userArg(user);
+ }
+
+ @OnChannelMessage("!channel")
+ public void channelArg(Channel channel) {
+ mock.channelArg(channel);
+ }
+
+ @OnChannelMessage("!event")
+ public void eventArg(ChannelMessageEvent event) {
+ mock.eventArg(event);
+ }
+
+ @OnChannelMessage("!nick @arg0")
+ public void nickArg(Nick arg0) {
+ mock.nickArg(arg0);
+ }
+
+ @OnChannelMessage("!multiarg @arg0 @arg1")
+ public void multiArg(Nick arg0, Integer arg1) {
+ mock.multiArg(arg0, arg1);
+ }
+
+ @OnChannelMessage("!returnSomething")
+ public String returnSomething() {
+ mock.returnSomething();
+ return "something";
+ }
+
+ @OnChannelMessage("!returnNewline")
+ public String returnNewline() {
+ mock.returnNewline();
+ return "Message\nwith\nnewlines.";
+ }
+
+ @NoHelp
+ @OnChannelMessage("!nohelp")
+ public void noHelp() {
+ mock.noHelp();
+ }
+}
diff --git a/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/MockClientCreator.java b/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/MockClientCreator.java
new file mode 100644
index 00000000..274cf645
--- /dev/null
+++ b/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/MockClientCreator.java
@@ -0,0 +1,59 @@
+package com.oldterns.irc.bot.test;
+
+import com.oldterns.irc.bot.services.ClientCreator;
+import org.kitteh.irc.client.library.Client;
+import org.kitteh.irc.client.library.event.helper.MessageEvent;
+import org.kitteh.irc.client.library.feature.CaseMapping;
+import org.kitteh.irc.client.library.feature.EventManager;
+import org.kitteh.irc.client.library.feature.ServerInfo;
+import org.mockito.Mockito;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Alternative;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+
+@ApplicationScoped
+@Alternative
+public class MockClientCreator implements ClientCreator {
+ static Client client;
+ static List> eventListeners = new ArrayList<>();
+
+ @Override
+ public Client createClient(String nick) {
+ if (client == null) {
+ client = Mockito.mock(Client.class);
+ System.out.println("Okay...");
+ EventManager eventManager = Mockito.mock(EventManager.class);
+ Mockito.when(client.getName()).thenReturn(nick);
+ Mockito.when(client.getNick()).thenReturn(nick);
+ Mockito.when(client.getEventManager()).thenReturn(eventManager);
+ ServerInfo serverInfo = Mockito.mock(ServerInfo.class);
+ Mockito.when(client.getServerInfo()).thenReturn(serverInfo);
+ Mockito.when(serverInfo.getCaseMapping()).thenReturn(CaseMapping.ASCII);
+ doAnswer(invocationOnMock -> {
+ Object object = invocationOnMock.getArgument(0);
+ Arrays.stream(object.getClass().getMethods())
+ .filter(method -> method.getParameterCount() == 1 && MessageEvent.class.isAssignableFrom(method.getParameterTypes()[0]))
+ .forEach(method -> eventListeners.add(event -> {
+ try {
+ if (method.getParameterTypes()[0].isAssignableFrom(event.getClass())) {
+ method.invoke(object, event);
+ }
+ } catch (InvocationTargetException | IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ }));
+
+ return null;
+ }).when(eventManager).registerEventListener(any());
+ }
+ return client;
+ }
+}
diff --git a/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/ServiceImplementationTest.java b/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/ServiceImplementationTest.java
new file mode 100644
index 00000000..3ed4a6a8
--- /dev/null
+++ b/quarkus-irc-bot/deployment/src/test/java/com/oldterns/irc/bot/test/ServiceImplementationTest.java
@@ -0,0 +1,220 @@
+package com.oldterns.irc.bot.test;
+
+import com.oldterns.irc.bot.Nick;
+import com.oldterns.irc.bot.services.ClientCreatorImpl;
+import io.quarkus.test.QuarkusUnitTest;
+import io.smallrye.mutiny.tuples.Functions;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.kitteh.irc.client.library.element.Channel;
+import org.kitteh.irc.client.library.element.ServerMessage;
+import org.kitteh.irc.client.library.element.User;
+import org.kitteh.irc.client.library.event.channel.ChannelMessageEvent;
+import org.kitteh.irc.client.library.event.user.PrivateMessageEvent;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.internal.verification.VerificationModeFactory.only;
+
+public class ServiceImplementationTest {
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .overrideConfigKey("vilebot.default.channel", "#channel")
+ .overrideConfigKey("vilebot.default.nick", "Bot")
+ .overrideConfigKey("vilebot.irc.server", "localhost")
+ .overrideConfigKey("quarkus.arc.selected-alternatives", "com.oldterns.irc.bot.test.MockClientCreator")
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClasses(ExampleService.class,
+ MockClientCreator.class)
+ .deleteClass(ClientCreatorImpl.class)
+ );
+
+ Functions.Function3 fireMessage;
+ BiFunction firePrivateMessage;
+
+ @BeforeEach
+ public void setup() {
+ fireMessage = (nick, channelName, message) -> {
+ User user = Mockito.mock(User.class);
+ Mockito.when(user.getNick()).thenReturn(nick);
+ Mockito.when(user.getClient()).thenReturn(MockClientCreator.client);
+
+ Channel channel = Mockito.mock(Channel.class);
+ Mockito.when(channel.getName()).thenReturn(channelName);
+ Mockito.when(channel.getMessagingName()).thenReturn(channelName);
+ Mockito.when(channel.getClient()).thenReturn(MockClientCreator.client);
+
+ ServerMessage serverMessage = Mockito.mock(ServerMessage.class);
+
+ ChannelMessageEvent event = new ChannelMessageEvent(MockClientCreator.client, serverMessage, user, channel, message);
+ MockClientCreator.eventListeners.forEach(listener -> listener.accept(event));
+ return event;
+ };
+
+ firePrivateMessage = (nick, message) -> {
+ User user = Mockito.mock(User.class);
+ Mockito.when(user.getNick()).thenReturn(nick);
+ Mockito.when(user.getClient()).thenReturn(MockClientCreator.client);
+
+ ServerMessage serverMessage = Mockito.mock(ServerMessage.class);
+
+ PrivateMessageEvent event = new PrivateMessageEvent(MockClientCreator.client, serverMessage, user, nick, message);
+ MockClientCreator.eventListeners.forEach(listener -> listener.accept(event));
+ return event;
+ };
+ }
+
+ @Test
+ public void testHelpChannel() {
+ ChannelMessageEvent event = fireMessage.apply("user", "#channel", "!help");
+ final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ Mockito.verify(event.getActor(), Mockito.atLeastOnce()).sendMessage(captor.capture());
+
+ String message = String.join("\n", captor.getAllValues());
+ assertThat(message).contains("Available Commands: \n");
+ assertThat(message).contains("Help: { !help }");
+ assertThat(message).contains("Example: ");
+ assertThat(message).contains("{ !noargs }");
+ assertThat(message).contains("{ !string }");
+ assertThat(message).contains("{ !int }");
+ assertThat(message).contains("{ !maybe ?[] }");
+ assertThat(message).contains("{ !list [,...] }");
+ assertThat(message).contains("{ !client }");
+ assertThat(message).contains("{ !user }");
+ assertThat(message).contains("{ !channel }");
+ assertThat(message).contains("{ !event }");
+ assertThat(message).contains("{ !nick }");
+ assertThat(message).contains("{ !multiarg }");
+ assertThat(message).contains("{ !returnSomething }");
+ assertThat(message).contains("{ !returnNewline }");
+
+ assertThat(message).doesNotContain("{ !nohelp }");
+ }
+
+ @Test
+ public void testHelpPrivate() {
+ PrivateMessageEvent event = firePrivateMessage.apply("user", "!help");
+ final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ Mockito.verify(event.getActor(), Mockito.atLeastOnce()).sendMessage(captor.capture());
+
+ String message = String.join("\n", captor.getAllValues());
+ assertThat(message).contains("Available Commands: \n");
+ assertThat(message).contains("Help: { !help }");
+ assertThat(message).contains("Example: ");
+ assertThat(message).contains("{ !noargs }");
+ assertThat(message).contains("{ !string }");
+ assertThat(message).contains("{ !int }");
+ assertThat(message).contains("{ !maybe ?[] }");
+ assertThat(message).contains("{ !list [,...] }");
+ assertThat(message).contains("{ !client }");
+ assertThat(message).contains("{ !user }");
+ assertThat(message).contains("{ !event }");
+ assertThat(message).contains("{ !nick }");
+ assertThat(message).contains("{ !multiarg }");
+ assertThat(message).contains("{ !returnSomething }");
+ assertThat(message).contains("{ !returnNewline }");
+
+ assertThat(message).doesNotContain("{ !nohelp }");
+ }
+
+ @Test
+ public void testCommandListeners() {
+ ExampleService exampleService = Mockito.mock(ExampleService.class);
+ ExampleService.INSTANCE.setMock(exampleService);
+ fireMessage.apply("user", "#channel", "Do not trigger anything");
+ Mockito.verifyNoInteractions(exampleService);
+
+ fireMessage.apply("user", "#notrigger", "!noargs");
+ Mockito.verifyNoInteractions(exampleService);
+
+ fireMessage.apply("user", "#channel", "!noargs");
+ Mockito.verify(exampleService, only()).noArgs();
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!noargs 10");
+ Mockito.verifyNoInteractions(exampleService);
+
+ fireMessage.apply("user", "#channel", "!string hi all!");
+ Mockito.verify(exampleService, only()).stringArg("hi all!");
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!int 20");
+ Mockito.verify(exampleService, only()).intArg(20);
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!int hello");
+ Mockito.verifyNoInteractions(exampleService);
+
+ fireMessage.apply("user", "#channel", "!maybe");
+ Mockito.verify(exampleService, only()).optionalIntArg(Optional.empty());
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!maybe 10");
+ Mockito.verify(exampleService, only()).optionalIntArg(Optional.of(10));
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!maybe hello");
+ Mockito.verifyNoInteractions(exampleService);
+
+ fireMessage.apply("user", "#channel", "!list 1,2,3");
+ Mockito.verify(exampleService, only()).listArg(List.of(1,2,3));
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!list 1,hello,3");
+ Mockito.verifyNoInteractions(exampleService);
+
+ fireMessage.apply("user", "#channel", "!client");
+ Mockito.verify(exampleService, only()).clientArg(MockClientCreator.client);
+ Mockito.reset(exampleService);
+
+ ChannelMessageEvent event = fireMessage.apply("user", "#channel", "!user");
+ Mockito.verify(exampleService, only()).userArg(event.getActor());
+ Mockito.reset(exampleService);
+
+ event = fireMessage.apply("user", "#channel", "!event");
+ Mockito.verify(exampleService, only()).eventArg(event);
+ Mockito.reset(exampleService);
+
+ event = fireMessage.apply("user", "#channel", "!channel");
+ Mockito.verify(exampleService, only()).channelArg(event.getChannel());
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!nick myNick");
+ Mockito.verify(exampleService, only()).nickArg(Nick.valueOf("myNick"));
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!nick myNick anotherNick");
+ Mockito.verifyNoInteractions(exampleService);
+
+ fireMessage.apply("user", "#channel", "!multiarg myNick 10");
+ Mockito.verify(exampleService, only()).multiArg(Nick.valueOf("myNick"), 10);
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!multiarg myNick");
+ Mockito.verifyNoInteractions(exampleService);
+
+ event = fireMessage.apply("user", "#channel", "!returnSomething");
+ Mockito.verify(exampleService, only()).returnSomething();
+ Mockito.verify(event.getChannel()).sendMessage("something");
+ Mockito.reset(exampleService);
+
+ event = fireMessage.apply("user", "#channel", "!returnNewline");
+ Mockito.verify(exampleService, only()).returnNewline();
+ Mockito.verify(event.getChannel()).sendMessage("Message");
+ Mockito.verify(event.getChannel()).sendMessage("with");
+ Mockito.verify(event.getChannel()).sendMessage("newlines.");
+ Mockito.reset(exampleService);
+
+ fireMessage.apply("user", "#channel", "!nohelp");
+ Mockito.verify(exampleService, only()).noHelp();
+ Mockito.reset(exampleService);
+ }
+}
diff --git a/quarkus-irc-bot/pom.xml b/quarkus-irc-bot/pom.xml
new file mode 100644
index 00000000..682ca9a4
--- /dev/null
+++ b/quarkus-irc-bot/pom.xml
@@ -0,0 +1,64 @@
+
+
+ 4.0.0
+
+ com.oldterns.vilebot
+ quarkus-irc-bot-parent
+ 0.0.1-SNAPSHOT
+ Quarkus IRC Bot - Parent
+
+ pom
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ UTF-8
+ UTF-8
+ 11
+ true
+ 2.1.1.Final
+ 8.0.0
+ 3.8.1
+
+
+
+ deployment
+ runtime
+
+
+
+
+
+ io.quarkus
+ quarkus-bom
+ ${quarkus.version}
+ pom
+ import
+
+
+ org.kitteh.irc
+ client-lib
+ ${kitteh.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+
+
+
diff --git a/quarkus-irc-bot/runtime/pom.xml b/quarkus-irc-bot/runtime/pom.xml
new file mode 100644
index 00000000..a48f3eeb
--- /dev/null
+++ b/quarkus-irc-bot/runtime/pom.xml
@@ -0,0 +1,91 @@
+
+
+ 4.0.0
+
+ com.oldterns.vilebot
+ quarkus-irc-bot-parent
+ 0.0.1-SNAPSHOT
+ ../pom.xml
+
+
+ quarkus-irc-bot
+ Quarkus IRC Bot - Runtime
+
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-core
+
+
+ io.quarkus
+ quarkus-vertx-core
+
+
+ io.quarkus
+ quarkus-netty
+
+
+ io.quarkus
+ quarkus-jackson
+
+
+ io.quarkus
+ quarkus-vertx
+
+
+ io.quarkus
+ quarkus-mutiny
+
+
+ io.quarkus
+ quarkus-smallrye-context-propagation
+
+
+
+
+ org.kitteh.irc
+ client-lib
+
+
+
+
+
+
+ io.quarkus
+ quarkus-bootstrap-maven-plugin
+ ${quarkus.version}
+
+
+
+ extension-descriptor
+
+ compile
+
+ ${project.groupId}:${project.artifactId}-deployment:${project.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${quarkus.version}
+
+
+
+
+
+
+
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/Nick.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/Nick.java
new file mode 100644
index 00000000..25e8bc2a
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/Nick.java
@@ -0,0 +1,61 @@
+package com.oldterns.irc.bot;
+
+import org.kitteh.irc.client.library.element.User;
+import org.kitteh.irc.client.library.event.helper.ActorEvent;
+
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Nick {
+ public static final String regex = "(?:x|)([a-zA-Z0-9]+)(?:\\p{Punct}+[a-zA-Z0-9]*|)";
+ final String nick;
+ private static final Pattern nickPattern = Pattern.compile( regex );
+
+ public Nick(String nick) {
+ this.nick = nick;
+ }
+
+ public String getBaseNick() {
+ Matcher nickMatcher = nickPattern.matcher( nick );
+ if ( nickMatcher.find() )
+ {
+ return nickMatcher.group( 1 );
+ }
+ return nick;
+ }
+
+ public String getFullNick() {
+ return nick;
+ }
+
+ public static Nick getNick(ActorEvent event) {
+ return getNick(event.getActor());
+ }
+
+ public static Nick getNick(User user) {
+ return new Nick(user.getNick());
+ }
+
+ public static Nick valueOf(String nick) {
+ return new Nick(nick);
+ }
+
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Nick nick1 = (Nick) o;
+ return getBaseNick().equals(nick1.getBaseNick());
+ }
+
+ @Override public int hashCode() {
+ return Objects.hash(getBaseNick());
+ }
+
+ @Override
+ public String toString() {
+ return getBaseNick();
+ }
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Bot.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Bot.java
new file mode 100644
index 00000000..6c98cdc7
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Bot.java
@@ -0,0 +1,9 @@
+package com.oldterns.irc.bot.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Bot {
+ String value();
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Delimiter.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Delimiter.java
new file mode 100644
index 00000000..f403bf22
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Delimiter.java
@@ -0,0 +1,9 @@
+package com.oldterns.irc.bot.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Delimiter {
+ String value();
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/NoHelp.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/NoHelp.java
new file mode 100644
index 00000000..23578713
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/NoHelp.java
@@ -0,0 +1,8 @@
+package com.oldterns.irc.bot.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NoHelp {
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnChannelMessage.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnChannelMessage.java
new file mode 100644
index 00000000..c1348c86
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnChannelMessage.java
@@ -0,0 +1,10 @@
+package com.oldterns.irc.bot.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OnChannelMessage {
+ String value();
+ String channel() default "<>";
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnMessage.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnMessage.java
new file mode 100644
index 00000000..886e6fec
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnMessage.java
@@ -0,0 +1,9 @@
+package com.oldterns.irc.bot.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OnMessage {
+ String value();
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnPrivateMessage.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnPrivateMessage.java
new file mode 100644
index 00000000..a5bc708c
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/OnPrivateMessage.java
@@ -0,0 +1,9 @@
+package com.oldterns.irc.bot.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OnPrivateMessage {
+ String value();
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Regex.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Regex.java
new file mode 100644
index 00000000..40bd42c7
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/annotations/Regex.java
@@ -0,0 +1,9 @@
+package com.oldterns.irc.bot.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Regex {
+ String value();
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/BotStartupService.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/BotStartupService.java
new file mode 100644
index 00000000..0c91bcf3
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/BotStartupService.java
@@ -0,0 +1,43 @@
+package com.oldterns.irc.bot.services;
+
+import io.quarkus.runtime.ShutdownEvent;
+import io.quarkus.runtime.StartupEvent;
+import org.kitteh.irc.client.library.Client;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+import java.util.HashMap;
+import java.util.Map;
+
+@ApplicationScoped
+public class BotStartupService {
+
+ @Inject
+ Instance ircServices;
+
+ @Inject
+ ClientCreator clientCreator;
+
+ Map botNameToClient = new HashMap<>();
+
+ public void onStartup(@Observes StartupEvent e) throws InterruptedException {
+ ircServices.stream().forEach(ircService -> {
+ Client client = botNameToClient.computeIfAbsent(ircService.botNick(),
+ clientCreator::createClient);
+ ircService.getChannelsToJoin().forEach(client::addChannel);
+ });
+ for (Client client : botNameToClient.values()) {
+ client.connect();
+ ircServices.stream().forEach(client.getEventManager()::registerEventListener);
+ Thread.sleep(1000);
+ }
+ }
+
+ public void onShutdown(@Observes ShutdownEvent shutdownEvent) {
+ for (Client client : botNameToClient.values()) {
+ client.shutdown();
+ }
+ }
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/ClientCreator.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/ClientCreator.java
new file mode 100644
index 00000000..2970b406
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/ClientCreator.java
@@ -0,0 +1,7 @@
+package com.oldterns.irc.bot.services;
+
+import org.kitteh.irc.client.library.Client;
+
+public interface ClientCreator {
+ public Client createClient(String nick);
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/ClientCreatorImpl.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/ClientCreatorImpl.java
new file mode 100644
index 00000000..65975586
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/ClientCreatorImpl.java
@@ -0,0 +1,27 @@
+package com.oldterns.irc.bot.services;
+
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.kitteh.irc.client.library.Client;
+import org.kitteh.irc.client.library.util.StsUtil;
+
+import javax.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class ClientCreatorImpl implements ClientCreator {
+
+ @ConfigProperty(name="vilebot.irc.server")
+ public String ircServer;
+
+ @ConfigProperty(name="vilebot.irc.port", defaultValue = "6667")
+ public Integer ircPort;
+
+ public Client createClient(String nick) {
+ return Client.builder()
+ .name(nick).nick(nick).realName(nick).user(nick)
+ .server().host(ircServer).port(ircPort, Client.Builder.Server.SecurityType.INSECURE)
+ .then()
+ .management().stsStorageManager(StsUtil.getDefaultStorageManager())
+ .then()
+ .build();
+ }
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/HelpService.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/HelpService.java
new file mode 100644
index 00000000..ae40b2e4
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/HelpService.java
@@ -0,0 +1,28 @@
+package com.oldterns.irc.bot.services;
+
+import com.oldterns.irc.bot.annotations.OnMessage;
+import org.kitteh.irc.client.library.element.User;
+
+import javax.enterprise.context.ApplicationScoped;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@ApplicationScoped
+public class HelpService {
+ Map> groupToCommandList = new HashMap<>();
+
+ public void addHelpCommand(String group, String command) {
+ groupToCommandList.computeIfAbsent(group, key -> new ArrayList<>()).add(command);
+ }
+
+ @OnMessage("!help")
+ public void help(User user) {
+ user.sendMessage("Available Commands: ");
+ groupToCommandList.entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .map(entry -> " " + entry.getKey() + ": " + String.join(" ", entry.getValue()))
+ .forEach(user::sendMessage);
+ }
+}
diff --git a/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/IRCService.java b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/IRCService.java
new file mode 100644
index 00000000..55f7d33c
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/java/com/oldterns/irc/bot/services/IRCService.java
@@ -0,0 +1,96 @@
+package com.oldterns.irc.bot.services;
+
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.kitteh.irc.client.library.Client;
+import org.kitteh.irc.client.library.defaults.DefaultClient;
+import org.kitteh.irc.client.library.util.Cutter;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+public abstract class IRCService {
+ @ConfigProperty(name="vilebot.default.nick")
+ public String defaultNick;
+
+ @ConfigProperty(name="vilebot.default.channel")
+ public String defaultChannel;
+
+ private Client bot;
+
+ public String botNick() {
+ return defaultNick;
+ }
+
+ public String getMainChannel() {
+ return defaultChannel;
+ }
+
+
+ private static List cutMessage(String message, int maxLengthInBytes) {
+ final Function byteLength = string -> string.getBytes(StandardCharsets.UTF_8).length;
+ if (byteLength.apply(message) <= maxLengthInBytes) {
+ return List.of(message);
+ }
+ // Taken from https://stackoverflow.com/a/48870243
+ List out = new ArrayList<>();
+ CharsetEncoder coder = StandardCharsets.UTF_8.newEncoder();
+ ByteBuffer chuck = ByteBuffer.allocate(maxLengthInBytes); // output buffer of required size
+ CharBuffer in = CharBuffer.wrap(message);
+ int pos = 0;
+ while(true) {
+ CoderResult cr = coder.encode(in, chuck, true); // try to encode as much as possible
+ int newpos = message.length() - in.length();
+ String s = message.substring(pos, newpos);
+ out.add(s); // add what has been encoded to the list
+ pos = newpos; // store new input position
+ chuck.rewind(); // and rewind output buffer
+ if (! cr.isOverflow()) {
+ break; // everything has been encoded
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Splits a message into individual lines to be sent to IRC
+ * (Limit 512 bytes per line, including command)
+ */
+ public static String[] splitIrcMessage(String message) {
+ final int IRC_MESSAGE_LIMIT = 486; // 512 bytes including command name
+ String[] messageLines = message.split("\n");
+ List parts = new ArrayList<>(messageLines.length);
+ for (String messageLine : messageLines) {
+ parts.addAll(cutMessage(messageLine, IRC_MESSAGE_LIMIT));
+ }
+ String[] messageParts = new String[parts.size()];
+ for (int i = 0; i < messageParts.length; i++) {
+ messageParts[i] = parts.get(i);
+ }
+ return messageParts;
+ }
+
+ public Collection getChannel(String channel) {
+ if ("<>".equals(channel)) {
+ return Collections.singleton(getMainChannel());
+ }
+ if ("<>".equals(channel)) {
+ return getChannelsToJoin();
+ }
+ if (channel.startsWith("${")) {
+ String channelSet = ConfigProvider.getConfig().getValue(channel.substring(2, channel.length() - 1), String.class);
+ return List.of(channelSet.split(","));
+ }
+ return List.of(channel.split(","));
+ }
+
+ public abstract Collection getChannelsToJoin();
+}
diff --git a/quarkus-irc-bot/runtime/src/main/resources/META-INF/beans.xml b/quarkus-irc-bot/runtime/src/main/resources/META-INF/beans.xml
new file mode 100644
index 00000000..8ebcd1aa
--- /dev/null
+++ b/quarkus-irc-bot/runtime/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/vilebot/.dockerignore b/vilebot/.dockerignore
new file mode 100644
index 00000000..94810d00
--- /dev/null
+++ b/vilebot/.dockerignore
@@ -0,0 +1,5 @@
+*
+!target/*-runner
+!target/*-runner.jar
+!target/lib/*
+!target/quarkus-app/*
\ No newline at end of file
diff --git a/vilebot/.gitignore b/vilebot/.gitignore
index a2553546..57eec4a2 100644
--- a/vilebot/.gitignore
+++ b/vilebot/.gitignore
@@ -1,6 +1,42 @@
-/target/
-/db/
-/db-backups/
-/log/
-/cfg/vilebot*.conf
-/files/fonts/
\ No newline at end of file
+#Maven
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+release.properties
+
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+.factorypath
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
+# Local environment
+/db
+/log
+.env
+.fonts
diff --git a/vilebot/.mvn/wrapper/MavenWrapperDownloader.java b/vilebot/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 00000000..e76d1f32
--- /dev/null
+++ b/vilebot/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if(mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if(mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if(!outputFile.getParentFile().exists()) {
+ if(!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/vilebot/.mvn/wrapper/maven-wrapper.properties b/vilebot/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..642d572c
--- /dev/null
+++ b/vilebot/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/vilebot/README.md b/vilebot/README.md
new file mode 100644
index 00000000..5828c21f
--- /dev/null
+++ b/vilebot/README.md
@@ -0,0 +1,163 @@
+# VileBot
+
+## System Requirements
+
+- Podman or Docker
+- Java 11 or higher
+
+## Using VileBot
+
+### Building
+
+Before building VileBot, ensure quarkus-irc-bot is built. To build VileBot, run the following command:
+```shell
+./mvnw clean install
+```
+
+### Running
+
+To run VileBot, run the following command:
+```shell
+./server-control.sh start
+```
+
+### Stopping
+
+To stop VileBot, run the following command:
+```shell
+./server-control.sh stop
+```
+
+### Dev Mode
+
+When developing VileBot, you might want
+to try Dev Mode. First, start a test Redis database using the following command:
+```shell
+./startTestRedisInstance.sh
+```
+Next, run this command to launch Quarkus Dev Mode:
+```shell
+./mvnw quarkus:dev
+```
+
+## VileBot File Structure
+
+### .env
+
+This file hold API keys and VileBot configuration. This file should *NEVER* be committed to the repository.
+
+### src/main/resources/application.properties
+
+This file is an example configuration for VileBot1. Properties in `.env` will overwrite properties in `application.properties`.
+
+### src/main/resources
+
+Contains all data files that VileBot needs in order to work.
+
+### db
+
+Store the redis database.
+
+### cfg
+
+Store the redis config.
+
+### log
+
+Store VileBot and Redis logs.
+
+### utils
+
+Database migration utils (no migration is currently necessary, but might be useful if a future database migration occurs).
+
+## VileBot Java Package Structure
+
+### database
+
+Store services that directly interact with Redis.
+
+### services
+
+Store services user interact with.
+
+#### services.admin
+
+Store services that require an active admin session to use.
+
+### util
+
+Store various utilities used in various services.
+
+## Testing
+
+Testing is done using JUnit 5, AssertJ and Mockito.
+
+### Mocking URLs
+
+Many VileBot services connect to URLs in order to function. To test these services, you need to mock the responses from the URLs. You can do this easily with the `TestUrlStreamHandler`:
+
+```java
+@QuarkusTest
+public class MyServiceTest
+{
+
+ @Inject
+ MyService myService;
+
+ @Inject
+ TestUrlStreamHandler urlStreamHandler;
+
+ @Test
+ public void testService()
+ throws MalformedURLException
+ {
+ urlStreamHandler.mockConnection( "http://example.com",
+ MyServiceTest.class.getResourceAsStream( "expected-response.html" ) );
+ assertThat( myService.execute() ).isEqualTo( "Expected Content" );
+ }
+}
+```
+
+## A brief overview of quarkus-irc-bot
+
+VileBot works via `quarkus-irc-bot`, which is a Quarkus extension contained in this repo. `quarkus-irc-bot` generate handlers for annotated methods. Annotations:
+
+- `@OnMessage(template)`: Generate a handler that is triggered on any message (private/channel). It will be registered on all bots.
+- `@OnChannelMessage(template, channel=${vilebot.default.channel})`: Generate a handler that is triggered on a message in a particular channel (defaults to the default channel). It will be registered on all bots in the channel.
+- `@OnPrivateMessage(template)`: Generate a handler that is triggered on private message only. It will be registered to all bots.
+- `@NoHelp`: Instructs `quarkus-irc-bot` to not add this service to the automatically generated `!help` command.
+
+### Template format
+
+A template is a regex with some additional features:
+
+- A message is automatically trimmed before being parsed by the regex.
+- The regex has an implicit starting `^` and ending `$` (i.e. it does not trigger if it occurs in the middle of a message).
+- It allows parameters, which are in the format `@javaIdentifier`. It expects a parameter with exactly that name to be present in the annotated method, which will be passed the parsed value.
+- Automatically passing optional parameters detailing who sent the message and where it was sent.
+
+For example:
+
+```java
+import javax.enterprise.context.ApplicationScoped;
+
+import com.oldterns.irc.bot.Nick;
+import com.oldterns.irc.bot.annotations.OnMessage;
+
+@ApplicationScoped
+public class MyService {
+
+ @OnMessage("!call @nick @amount")
+ public String theMethod(User user, Nick nick, Integer amount) {
+ return Nick.getNick(user).getBaseNick() + " has called " + nick + " for " + amount + " karma!";
+ }
+}
+```
+
+Creates a regex similar to `^!call (?[A-Za-z0-9]+) (?[0-9]+)$`. When `bob` sends the message "!call alice 100", `theMethod` is called with `(User(bob), Nick(alice), 100).
+
+### Automatic parameters
+
+- `org.kitteh.irc.client.library.element.User`: The user that sent the message. Has a `sendMessage(String)` method that can be used to private message the user.
+- `org.kitteh.irc.client.library.element.Channel`: (`@OnChannelMessage` only) The channel the message was sent at. Can be used to send future replies.
+- `org.kitteh.irc.client.library.event.channel.ChannelMessageEvent`: (`@OnChannelMessage` only) the source event.
\ No newline at end of file
diff --git a/vilebot/cfg/vilebot.conf.example b/vilebot/cfg/vilebot.conf.example
deleted file mode 100644
index 20f9fba6..00000000
--- a/vilebot/cfg/vilebot.conf.example
+++ /dev/null
@@ -1,42 +0,0 @@
-logLevel=TRACE
-
-redisHost=localhost
-redisPort=6300
-redisPassword=
-
-ircBotAmount=1
-
-ircUser1=ExampleBot
-ircNick1=ExampleBot
-ircRealName1=A Bot
-ircServerAddress1=irc.example.com
-ircPort1=6697
-ircSslMode1=ON_NOHOST
-ircChannel1=#examplebot
-ircChannelAutoOp1=false
-
-# Retrieve these sections from https://apps.twitter.com
-# for twitter integration.
-consumerKey=
-consumerSecret=
-accessToken=
-accessTokenSecret=
-
-# Retrieve from https://words.bighugelabs.com/getkey.php
-# for jaziz intergration.
-thesaurusKey=
-
-# Retrieve from https://developer.wolframalpha.com/portal/apisignup.html
-# for answerQuestion intergration.
-wolframKey=
-
-rpsChannel=#exampleGameChannel
-countdownChannel=#exampleGameChannel
-jeopardyChannel=#exampleGameChannel
-OmgwordChannel=#exampleGameChannel
-OmgwordList=files/wordlist.txt
-FortuneList=files/fortunelist.txt
-InspirationList=files/inspirationslist.txt
-InspirationIndex=files/inspirationsindex.txt
-
-pastebinApiUrl=
\ No newline at end of file
diff --git a/vilebot/download-fonts.sh b/vilebot/download-fonts.sh
deleted file mode 100755
index 29abd9ec..00000000
--- a/vilebot/download-fonts.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-mkdir files/fonts
-for font in `cat files/fontlist.txt`;
-do
- wget -P files/fonts/ $font
-done
diff --git a/vilebot/license-header.txt b/vilebot/license-header.txt
deleted file mode 100644
index 46eda259..00000000
--- a/vilebot/license-header.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Copyright (C) 2013 Oldterns
-
-This file may be modified and distributed under the terms
-of the MIT license. See the LICENSE file for details.
diff --git a/vilebot/mvnw b/vilebot/mvnw
new file mode 100755
index 00000000..a16b5431
--- /dev/null
+++ b/vilebot/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/vilebot/mvnw.cmd b/vilebot/mvnw.cmd
new file mode 100755
index 00000000..c8d43372
--- /dev/null
+++ b/vilebot/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/vilebot/pom.xml b/vilebot/pom.xml
index 0a9a2500..64249a11 100644
--- a/vilebot/pom.xml
+++ b/vilebot/pom.xml
@@ -1,167 +1,214 @@
-
- 4.0.0
- com.oldterns.vilebot
- vilebot
- 0.0.1-SNAPSHOT
- Vilebot
- Vilebot Keratin
+
+
+ 4.0.0
+ com.oldterns.vilebot
+ vilebot
+ 0.0.1-SNAPSHOT
+
+ 3.8.1
+ true
+ 11
+ 11
+ UTF-8
+ UTF-8
+ 2.1.1.Final
+ quarkus-universe-bom
+ io.quarkus
+ 2.1.1.Final
+ 3.0.0-M5
+ 1.9.0
+ 2.8.1
+ 3.12.0
+
-
- UTF-8
-
+
+
+
+ io.quarkus
+ quarkus-bom
+ ${quarkus.platform.version}
+ pom
+ import
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${apache-commons.version}
+
+
+
+
+
+
+ com.oldterns.vilebot
+ quarkus-irc-bot
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-jackson
+
+
+ io.quarkus
+ quarkus-redis-client
+
-
- src/main/java
- src/test/java
+
+ org.apache.commons
+ commons-lang3
+
+
+ rome
+ rome
+ 1.0
+
+
+ org.jsoup
+ jsoup
+ 1.8.1
+
+
+ org.twitter4j
+ twitter4j-core
+ [4.0,)
+
+
+ info.debatty
+ java-string-similarity
+ 0.17
+ compile
+
+
+ org.beanshell
+ bsh
+ 2.0b4
+
+
+ com.github.lalyos
+ jfiglet
+ 0.0.8
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.19.0
+ test
+
+
+ io.quarkus
+ quarkus-junit5-mockito
+ test
+
+
+
+
+
+ com.mycila.maven-license-plugin
+ maven-license-plugin
+ ${maven-license-plugin.version}
+
+
+ true
+
+ src/**
+
+
+
+
+ net.revelc.code.formatter
+ formatter-maven-plugin
+ ${formatter-maven-plugin.version}
+
+ ${project.basedir}/maven-eclipse-codestyle.xml
+
+
+
+
+ format
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+ ${quarkus-plugin.version}
+ true
+
+
+
+ build
+ generate-code
+ generate-code-tests
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+ ${maven.compiler.parameters}
+
+
+
+ maven-surefire-plugin
+ ${surefire-plugin.version}
+
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
-
- org.apache.maven.plugins
- maven-shade-plugin
- 2.1
-
- ${project.build.directory}/vilebot-shaded.jar
-
-
- com.oldterns.vilebot.Vilebot
-
-
-
-
-
- package
-
- shade
-
-
-
-
-
-
-
-
-
-
-
-
-
- com.mycila.maven-license-plugin
- maven-license-plugin
- 1.9.0
+
+ maven-failsafe-plugin
+ ${surefire-plugin.version}
+
+
+
+ integration-test
+ verify
+
-
- true
-
- src/**
-
+
+ ${project.build.directory}/${project.build.finalName}-runner
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
-
-
- net.revelc.code.formatter
- formatter-maven-plugin
- 2.8.1
-
- ${project.basedir}/maven-eclipse-codestyle.xml
-
-
-
-
- format
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.8.0
-
- 11
- 11
-
-
+
+
+
-
-
-
-
- redis.clients
- jedis
- 2.1.0
-
-
- rome
- rome
- 1.0
-
-
- org.twitter4j
- twitter4j-core
- [4.0,)
-
-
- org.jsoup
- jsoup
- 1.8.1
-
-
- info.debatty
- java-string-similarity
- 0.17
-
-
- junit
- junit
- 4.11
-
-
- org.mockito
- mockito-core
- 2.23.4
-
-
- org.beanshell
- bsh
- 2.0b4
-
-
- com.github.lalyos
- jfiglet
- 0.0.8
-
-
- org.apache.commons
- commons-configuration2
- 2.7
-
-
- commons-beanutils
- commons-beanutils
- 1.9.4
-
-
- org.pircbotx
- pircbotx
- 2.1
-
-
- org.slf4j
- slf4j-simple
- 1.7.26
-
-
- com.google.code.gson
- gson
- 2.8.5
-
-
- org.apache.httpcomponents
- httpclient
- 4.5.8
-
-
- org.apache.commons
- commons-lang3
- 3.9
-
-
+
+
+ native
+
+
+
diff --git a/vilebot/server-control.sh b/vilebot/server-control.sh
index 202bf80f..670f984d 100755
--- a/vilebot/server-control.sh
+++ b/vilebot/server-control.sh
@@ -14,7 +14,7 @@ set -f
: "${VB_REDIS_CONF_PATH:=cfg/redis.conf}"
: "${VB_REDIS_PID_PATH:=/tmp/vb-redis-server-pid-$USER}"
-: "${VB_JAR_PATH:=target/vilebot-shaded.jar}"
+: "${VB_JAR_PATH:=target/quarkus-app/quarkus-run.jar}"
: "${VB_PID_PATH:=/tmp/vb-server-pid-$USER}"
: "${VB_LOG_PATH:=log}"
: "${VB_REMOTE_DEBUG:=0}"
@@ -64,7 +64,7 @@ mode_start() {
msg ">> Testing prerequisites"
local required_files=("$VB_JAR_PATH"
"$VB_REDIS_CONF_PATH"
- "cfg/vilebot.conf")
+ ".env")
for filepath in "${required_files[@]}"
do
@@ -76,7 +76,6 @@ mode_start() {
fi
done
- mkdir -p db
mkdir -p log
status_info=$(mode_status | grep -e ' up$' || true)
@@ -86,7 +85,7 @@ mode_start() {
msg ">> Redis server already up"
else
msg ">> Starting local redis server"
- nohup redis-server "$VB_REDIS_CONF_PATH" 1>>"$VB_LOG_PATH/redis-stdout.log" 2>&1 &
+ nohup ./startProdRedisInstance.sh >> log/redis.log 2>&1 &
echo -n "$!" >| "$VB_REDIS_PID_PATH"
fi
@@ -119,6 +118,24 @@ mode_start() {
fi
}
+get_docker() {
+ local DOCKER=''
+ if command -v docker &> /dev/null
+ then
+ DOCKER='docker'
+ fi
+ if command -v podman &> /dev/null
+ then
+ DOCKER='podman'
+ fi
+ if [ -z "$DOCKER" ]
+ then
+ echo "Could not find docker or podman. Aborting"
+ exit 1
+ fi
+ echo $DOCKER
+}
+
mode_stop() {
msg "Stopping services"
@@ -138,12 +155,17 @@ mode_stop() {
fi
msg ">> Stopping local redis server"
- redis-cli -p "$(get_redis_port)" SHUTDOWN || msg ">> Couldn't send shutdown command to redis server"
+ kill -15 "$redis_pid" || msg ">> Couldn't send term signal to redis"
+ while kill -0 "$redis_pid" >/dev/null 2>&1
+ do
+ sleep 1
+ done
+ $(get_docker) kill --signal TERM redis_quarkus_prod >/dev/null 2>&1
}
mode_status() {
_is_redis_up() {
- redis-cli -p "$(get_redis_port)" PING >/dev/null 2>&1
+ $(get_docker) container exists redis_quarkus_prod
}
_is_vilebot_up() {
@@ -171,6 +193,7 @@ mode_status() {
fi
true
+
}
mode_restart() {
diff --git a/vilebot/src/main/docker/Dockerfile.jvm b/vilebot/src/main/docker/Dockerfile.jvm
new file mode 100644
index 00000000..f7dc2403
--- /dev/null
+++ b/vilebot/src/main/docker/Dockerfile.jvm
@@ -0,0 +1,54 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./mvnw package
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/code-with-quarkus-jvm .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-jvm
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/code-with-quarkus-jvm
+#
+###
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
+
+ARG JAVA_PACKAGE=java-11-openjdk-headless
+ARG RUN_JAVA_VERSION=1.3.8
+ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
+# Install java and the run-java script
+# Also set up permissions for user `1001`
+RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
+ && microdnf update \
+ && microdnf clean all \
+ && mkdir /deployments \
+ && chown 1001 /deployments \
+ && chmod "g+rwX" /deployments \
+ && chown 1001:root /deployments \
+ && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
+ && chown 1001 /deployments/run-java.sh \
+ && chmod 540 /deployments/run-java.sh \
+ && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
+
+# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
+ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+# We make four distinct layers so if there are application changes the library layers can be re-used
+COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/
+COPY --chown=1001 target/quarkus-app/*.jar /deployments/
+COPY --chown=1001 target/quarkus-app/app/ /deployments/app/
+COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/
+
+EXPOSE 8080
+USER 1001
+
+ENTRYPOINT [ "/deployments/run-java.sh" ]
diff --git a/vilebot/src/main/docker/Dockerfile.legacy-jar b/vilebot/src/main/docker/Dockerfile.legacy-jar
new file mode 100644
index 00000000..5df659c7
--- /dev/null
+++ b/vilebot/src/main/docker/Dockerfile.legacy-jar
@@ -0,0 +1,51 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./mvnw package -Dquarkus.package.type=legacy-jar
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/code-with-quarkus-legacy-jar .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-legacy-jar
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/code-with-quarkus-legacy-jar
+#
+###
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
+
+ARG JAVA_PACKAGE=java-11-openjdk-headless
+ARG RUN_JAVA_VERSION=1.3.8
+ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
+# Install java and the run-java script
+# Also set up permissions for user `1001`
+RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
+ && microdnf update \
+ && microdnf clean all \
+ && mkdir /deployments \
+ && chown 1001 /deployments \
+ && chmod "g+rwX" /deployments \
+ && chown 1001:root /deployments \
+ && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
+ && chown 1001 /deployments/run-java.sh \
+ && chmod 540 /deployments/run-java.sh \
+ && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
+
+# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
+ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+COPY target/lib/* /deployments/lib/
+COPY target/*-runner.jar /deployments/app.jar
+
+EXPOSE 8080
+USER 1001
+
+ENTRYPOINT [ "/deployments/run-java.sh" ]
diff --git a/vilebot/src/main/docker/Dockerfile.native b/vilebot/src/main/docker/Dockerfile.native
new file mode 100644
index 00000000..90b8c68d
--- /dev/null
+++ b/vilebot/src/main/docker/Dockerfile.native
@@ -0,0 +1,27 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
+#
+# Before building the container image run:
+#
+# ./mvnw package -Pnative
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native -t quarkus/code-with-quarkus .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus
+#
+###
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
+WORKDIR /work/
+RUN chown 1001 /work \
+ && chmod "g+rwX" /work \
+ && chown 1001:root /work
+COPY --chown=1001:root target/*-runner /work/application
+
+EXPOSE 8080
+USER 1001
+
+CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/vilebot/src/main/docker/Dockerfile.native-distroless b/vilebot/src/main/docker/Dockerfile.native-distroless
new file mode 100644
index 00000000..5fda9896
--- /dev/null
+++ b/vilebot/src/main/docker/Dockerfile.native-distroless
@@ -0,0 +1,23 @@
+####
+# This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode
+#
+# Before building the container image run:
+#
+# ./mvnw package -Pnative
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/code-with-quarkus .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus
+#
+###
+FROM quay.io/quarkus/quarkus-distroless-image:1.0
+COPY target/*-runner /application
+
+EXPOSE 8080
+USER nonroot
+
+CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/Vilebot.java b/vilebot/src/main/java/com/oldterns/vilebot/Vilebot.java
deleted file mode 100644
index e7d1eac2..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/Vilebot.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- Copyright (C) 2013 Oldterns
-
- This file may be modified and distributed under the terms
- of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import com.oldterns.vilebot.handlers.admin.AdminManagement;
-import com.oldterns.vilebot.handlers.admin.AdminPing;
-import com.oldterns.vilebot.handlers.admin.Auth;
-import com.oldterns.vilebot.handlers.admin.GetLog;
-import com.oldterns.vilebot.handlers.admin.NickChange;
-import com.oldterns.vilebot.handlers.admin.Quit;
-import com.oldterns.vilebot.handlers.user.AnswerQuestion;
-import com.oldterns.vilebot.handlers.user.Ascii;
-import com.oldterns.vilebot.handlers.user.ChatLogger;
-import com.oldterns.vilebot.handlers.user.Church;
-import com.oldterns.vilebot.handlers.user.Countdown;
-import com.oldterns.vilebot.handlers.user.Decide;
-import com.oldterns.vilebot.handlers.user.DownOrJustMe;
-import com.oldterns.vilebot.handlers.user.Excuses;
-import com.oldterns.vilebot.handlers.user.FakeNews;
-import com.oldterns.vilebot.handlers.user.Fortune;
-import com.oldterns.vilebot.handlers.user.GetInfoOn;
-import com.oldterns.vilebot.handlers.user.Help;
-import com.oldterns.vilebot.handlers.user.ImageToAscii;
-import com.oldterns.vilebot.handlers.user.Inspiration;
-import com.oldterns.vilebot.handlers.user.Jaziz;
-import com.oldterns.vilebot.handlers.user.Jokes;
-import com.oldterns.vilebot.handlers.user.Kaomoji;
-import com.oldterns.vilebot.handlers.user.Karma;
-import com.oldterns.vilebot.handlers.user.KarmaRoll;
-import com.oldterns.vilebot.handlers.user.KarmaTransfer;
-import com.oldterns.vilebot.handlers.user.LastMessageSed;
-import com.oldterns.vilebot.handlers.user.LastSeen;
-import com.oldterns.vilebot.handlers.user.Markov;
-import com.oldterns.vilebot.handlers.user.News;
-import com.oldterns.vilebot.handlers.user.Omgword;
-import com.oldterns.vilebot.handlers.user.Ops;
-import com.oldterns.vilebot.handlers.user.QuotesAndFacts;
-import com.oldterns.vilebot.handlers.user.RemindMe;
-import com.oldterns.vilebot.handlers.user.RockPaperScissors;
-import com.oldterns.vilebot.handlers.user.Summon;
-import com.oldterns.vilebot.handlers.user.Trivia;
-import com.oldterns.vilebot.handlers.user.Ttc;
-import com.oldterns.vilebot.handlers.user.TwitterCorrection;
-import com.oldterns.vilebot.handlers.user.UrlTitleAnnouncer;
-import com.oldterns.vilebot.handlers.user.UrlTweetAnnouncer;
-import com.oldterns.vilebot.handlers.user.UserPing;
-import com.oldterns.vilebot.handlers.user.Userlists;
-import com.oldterns.vilebot.handlers.user.Weather;
-import com.oldterns.vilebot.util.BaseNick;
-import org.pircbotx.Configuration;
-import org.pircbotx.MultiBotManager;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import redis.clients.jedis.JedisPool;
-import redis.clients.jedis.JedisPoolConfig;
-import redis.clients.jedis.Protocol;
-
-public class Vilebot
- extends ListenerAdapter
-{
-
- private static final Logger logger = LoggerFactory.getLogger( Vilebot.class );
-
- private static final String BOT_CONFIG_FILE = "cfg/vilebot.conf";
-
- private static Map cfg = getConfigMap();
-
- private static JedisPool pool;
-
- public static void main( String[] args )
- {
- // Database
- String redisHost = cfg.get( "redisHost" );
- int redisPort = Integer.parseInt( cfg.get( "redisPort" ) );
- String redisPassword = cfg.get( "redisPassword" );
- if ( redisPassword.length() > 0 )
- {
- pool =
- new JedisPool( new JedisPoolConfig(), redisHost, redisPort, Protocol.DEFAULT_TIMEOUT, redisPassword );
- }
- else
- {
- pool = new JedisPool( new JedisPoolConfig(), redisHost, redisPort );
- }
-
- Runtime.getRuntime().addShutdownHook( new Thread( () -> pool.destroy() ) );
-
- BaseNick.setPrimaryBotNick( cfg.get( "ircNick1" ) );
-
- MultiBotManager botManager = new MultiBotManager();
-
- // Bot
- int ircBotAmount = Integer.parseInt( cfg.get( "ircBotAmount" ) );
- for ( int i = 1; i <= ircBotAmount; i++ )
- {
- String ircUser = cfg.get( "ircUser" + i );
- String ircNick = cfg.get( "ircNick" + i );
- String ircRealName = cfg.get( "ircRealName" + i );
- String ircServerAddress = cfg.get( "ircServerAddress" + i );
- int ircPort = Integer.parseInt( cfg.get( "ircPort" + i ) );
- String ircChannel = cfg.get( "ircChannel" + i );
-
- BaseNick.addBotNick( ircNick );
-
- Configuration botConfiguration =
- new Configuration.Builder().setName( ircNick ).setLogin( ircUser ).setRealName( ircRealName ).addServer( ircServerAddress,
- ircPort ).addAutoJoinChannel( ircChannel ).setAutoReconnect( true ).addListener( new Vilebot() ).addListener( new AdminManagement() ).addListener( new AdminPing() ).addListener( new Auth() ).addListener( new DownOrJustMe() ).addListener( new GetLog() ).addListener( new com.oldterns.vilebot.handlers.admin.Help() ).addListener( new NickChange() ).addListener( new com.oldterns.vilebot.handlers.admin.Ops() ).addListener( new Quit() ).addListener( new AnswerQuestion() ).addListener( new Ascii() ).addListener( new ChatLogger() ).addListener( new Church() ).addListener( new Countdown() ).addListener( new Decide() ).addListener( new Excuses() ).addListener( new FakeNews() ).addListener( new Fortune() ).addListener( new GetInfoOn() ).addListener( new Help() ).addListener( new ImageToAscii() ).addListener( new Inspiration() ).addListener( new Jaziz() ).addListener( new Jokes() ).addListener( new Kaomoji() ).addListener( new Karma() ).addListener( new KarmaRoll() ).addListener( new KarmaTransfer() ).addListener( new LastMessageSed() ).addListener( new LastSeen() ).addListener( new Markov() ).addListener( new News() ).addListener( new Omgword() ).addListener( new Ops() ).addListener( new QuotesAndFacts() ).addListener( new RemindMe() ).addListener( new RockPaperScissors() ).addListener( new Summon() ).addListener( new Trivia() ).addListener( new Ttc() ).addListener( new TwitterCorrection() ).addListener( new UrlTitleAnnouncer() ).addListener( new UrlTweetAnnouncer() ).addListener( new Userlists() ).addListener( new UserPing() ).addListener( new Weather() ).buildConfiguration();
-
- botManager.addBot( botConfiguration );
- }
-
- botManager.start();
- // Done
- }
-
- private static Map getConfigMap()
- {
- Map cfg = new HashMap<>();
- Properties prop = new Properties();
- InputStream input = null;
- try
- {
- input = new FileInputStream( BOT_CONFIG_FILE );
- prop.load( input );
- Enumeration> e = prop.propertyNames();
- while ( e.hasMoreElements() )
- {
- String key = (String) e.nextElement();
- String val = prop.getProperty( key );
- cfg.put( key, val );
- }
- }
- catch ( IOException e )
- {
- logger.error( e.getMessage() );
- }
- finally
- {
- if ( input != null )
- {
- try
- {
- input.close();
- }
- catch ( IOException e )
- {
- logger.error( e.getMessage() );
- }
- }
- }
- return cfg;
- }
-
- public static JedisPool getPool()
- {
- return pool;
- }
-
- public static Map getConfig()
- {
- return new HashMap<>( cfg );
- }
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- logger.info( String.format( "[%s] %s", event.getUser().getNick(), event.getMessage() ) );
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/ChurchDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/ChurchDB.java
new file mode 100644
index 00000000..3728e902
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/ChurchDB.java
@@ -0,0 +1,159 @@
+package com.oldterns.vilebot.database;
+
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * This class is effectively a duplicate of KarmaDB, used for church related karma
+ */
+@ApplicationScoped
+public class ChurchDB
+{
+ private static final String keyOfChurchDonorSortedSet = "church-donor-karma";
+
+ private static final String keyOfChurchSortedSet = "church-karma";
+
+ private static final String keyOfChurchDonorTitleSortedSet = "church-title-";
+
+ @Inject
+ RedisClient redisClient;
+
+ public boolean isTopDonor( String noun )
+ {
+ return getDonorRank( noun ).map( rank -> rank <= 4 ).orElse( false );
+ }
+
+ /**
+ * Change the karma of a noun by an integer.
+ *
+ * @param noun The noun to change the karma of
+ * @param mod The amount to change the karma by, may be negative.
+ */
+ public void modDonorKarma( String noun, Integer mod )
+ {
+ redisClient.zincrby( keyOfChurchDonorSortedSet, mod.toString(), noun );
+ }
+
+ /**
+ * Change the karma not affiliated with a donor
+ */
+ public void modNonDonorKarma( Integer mod )
+ {
+ redisClient.zincrby( keyOfChurchSortedSet, mod.toString(), keyOfChurchSortedSet );
+ }
+
+ /**
+ * Change the title of noun to a string.
+ *
+ * @param noun The noun to change the karma of
+ * @param newTitle The string to change the title to
+ */
+ public void modDonorTitle( String noun, String newTitle )
+ {
+ Long titleCount = redisClient.scard( keyOfChurchDonorTitleSortedSet + noun ).toLong();
+ for ( Long i = 0L; i < titleCount; i++ )
+ {
+ redisClient.spop( List.of( keyOfChurchDonorTitleSortedSet + noun ) );
+ }
+ redisClient.sadd( List.of( keyOfChurchDonorTitleSortedSet + noun, newTitle ) );
+ }
+
+ /**
+ * Get the karma of a noun.
+ *
+ * @param noun The noun to query to karma of
+ * @return Integer iff the noun has a defined value, else null
+ */
+ public Optional getDonorKarma( String noun )
+ {
+ return Optional.ofNullable( redisClient.zscore( keyOfChurchDonorSortedSet, noun ) ).map( Response::toLong );
+ }
+
+ /**
+ * Get the rank of a noun based on its karma.
+ *
+ * @param noun The noun to query the rank of
+ * @return Integer iff the noun has a defined value, else null
+ */
+ public Optional getDonorRank( String noun )
+ {
+ return Optional.ofNullable( redisClient.zrevrank( keyOfChurchDonorSortedSet, noun ) ).map( rank -> rank.toLong()
+ + 1 );
+ }
+
+ /**
+ * Get the title of a noun.
+ *
+ * @param noun The noun to query the rank of
+ * @return String iff the noun has a defined value, else null
+ */
+ public String getDonorTitle( String noun )
+ {
+ return redisClient.srandmember( List.of( keyOfChurchDonorTitleSortedSet + noun ) ).toString();
+ }
+
+ /**
+ * Get nouns from karma ranks.
+ *
+ * @param lower The lower rank to get the nouns of.
+ * @param upper The upper rank to get the nouns of.
+ * @return String The noun iff the rank exists, else null.
+ */
+ public Set getDonorsByRanks( Long lower, Long upper )
+ {
+ Set nouns =
+ redisClient.zrevrange( List.of( keyOfChurchDonorSortedSet, lower.toString(),
+ upper.toString() ) ).stream().map( Response::toString ).collect( Collectors.toSet() );
+
+ if ( nouns.size() == 0 )
+ {
+ return null;
+ }
+
+ return nouns;
+ }
+
+ public boolean removeDonor( String noun )
+ {
+ Long existed = redisClient.zrem( List.of( keyOfChurchDonorSortedSet, noun ) ).toLong();
+ if ( Long.valueOf( existed ) != 1 )
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public long getTotalDonations()
+ {
+ Set members =
+ redisClient.zrange( List.of( keyOfChurchDonorSortedSet, "0",
+ "-1" ) ).stream().map( Response::toString ).collect( Collectors.toSet() );
+ return sum( keyOfChurchDonorSortedSet, members );
+ }
+
+ public long getTotalNonDonations()
+ {
+ Set members =
+ redisClient.zrange( List.of( keyOfChurchSortedSet, "0",
+ "-1" ) ).stream().map( Response::toString ).collect( Collectors.toSet() );
+ return sum( keyOfChurchSortedSet, members );
+ }
+
+ private long sum( String set, Set members )
+ {
+ long sum = 0;
+ for ( String member : members )
+ {
+ sum += redisClient.zscore( set, member ).toLong();
+ }
+ return sum;
+ }
+}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/ExcuseDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/ExcuseDB.java
new file mode 100644
index 00000000..bc5ad757
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/ExcuseDB.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2013 Oldterns
+ *
+ * This file may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+package com.oldterns.vilebot.database;
+
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+import org.jsoup.Connection;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Optional;
+
+@ApplicationScoped
+public class ExcuseDB
+{
+ private static final String keyOfExcuseSet = "excuses";
+
+ @Inject
+ RedisClient redisClient;
+
+ public void addExcuse( String excuse )
+ {
+ redisClient.sadd( List.of( keyOfExcuseSet, excuse ) );
+ }
+
+ public String getRandExcuse()
+ {
+ return Optional.ofNullable( redisClient.srandmember( List.of( keyOfExcuseSet ) ) ).map( Response::toString ).orElse( null );
+ }
+}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/GroupDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/GroupDB.java
similarity index 61%
rename from vilebot/src/main/java/com/oldterns/vilebot/db/GroupDB.java
rename to vilebot/src/main/java/com/oldterns/vilebot/database/GroupDB.java
index cc304f8f..708a376f 100644
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/GroupDB.java
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/GroupDB.java
@@ -4,12 +4,16 @@
* This file may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
-package com.oldterns.vilebot.db;
+package com.oldterns.vilebot.database;
-import redis.clients.jedis.Jedis;
+import io.quarkus.redis.client.RedisClient;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+
+@ApplicationScoped
public class GroupDB
- extends RedisDB
{
private static final String keyOfGroupSetsPrefix = "groups-";
@@ -17,12 +21,15 @@ public class GroupDB
private static final String adminGroupName = "admins";
+ @Inject
+ RedisClient redisClient;
+
/**
* Checks if the the Admin group is empty.
*
* @return true iff the Admin group has no members.
*/
- public static boolean noAdmins()
+ public boolean noAdmins()
{
return groupSize( adminGroupName ) == 0;
}
@@ -33,7 +40,7 @@ public static boolean noAdmins()
* @param nick The nick to check
* @return true iff the nick is in the Admin group
*/
- public static boolean isAdmin( String nick )
+ public boolean isAdmin( String nick )
{
return isInGroup( adminGroupName, nick );
}
@@ -44,7 +51,7 @@ public static boolean isAdmin( String nick )
* @param nick The nick to check
* @return true iff the nick is in the Op group
*/
- public static boolean isOp( String nick )
+ public boolean isOp( String nick )
{
return isInGroup( opGroupName, nick );
}
@@ -56,30 +63,14 @@ public static boolean isOp( String nick )
* @param nick The nick to check
* @return true iff the nick is in the Op group
*/
- private static boolean isInGroup( String group, String nick )
+ private boolean isInGroup( String group, String nick )
{
- Jedis jedis = pool.getResource();
- try
- {
- return nick != null && jedis.sismember( keyOfGroupSetsPrefix + group, nick );
- }
- finally
- {
- pool.returnResource( jedis );
- }
+ return nick != null && redisClient.sismember( keyOfGroupSetsPrefix + group, nick ).toBoolean();
}
- private static long groupSize( String group )
+ private long groupSize( String group )
{
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.scard( keyOfGroupSetsPrefix + group );
- }
- finally
- {
- pool.returnResource( jedis );
- }
+ return redisClient.scard( keyOfGroupSetsPrefix + group ).toLong();
}
/**
@@ -88,7 +79,7 @@ private static long groupSize( String group )
* @param nick The nick to add
* @return true iff the nick was not already there
*/
- public static boolean addAdmin( String nick )
+ public boolean addAdmin( String nick )
{
return addToGroup( adminGroupName, nick );
}
@@ -99,7 +90,7 @@ public static boolean addAdmin( String nick )
* @param nick The nick to add
* @return true iff the nick was not already there
*/
- public static boolean addOp( String nick )
+ public boolean addOp( String nick )
{
return addToGroup( opGroupName, nick );
}
@@ -111,18 +102,10 @@ public static boolean addOp( String nick )
* @param nick The nick to add
* @return true iff a new element was inserted
*/
- private static boolean addToGroup( String group, String nick )
+ private boolean addToGroup( String group, String nick )
{
- Jedis jedis = pool.getResource();
- try
- {
- long reply = jedis.sadd( keyOfGroupSetsPrefix + group, nick );
- return reply == 1;
- }
- finally
- {
- pool.returnResource( jedis );
- }
+ long reply = redisClient.sadd( List.of( keyOfGroupSetsPrefix + group, nick ) ).toLong();
+ return reply == 1;
}
/**
@@ -131,7 +114,7 @@ private static boolean addToGroup( String group, String nick )
* @param nick The nick to remove
* @return true iff the nick existed
*/
- public static boolean remAdmin( String nick )
+ public boolean remAdmin( String nick )
{
return remFromGroup( adminGroupName, nick );
}
@@ -142,7 +125,7 @@ public static boolean remAdmin( String nick )
* @param nick The nick to remove
* @return true iff the nick existed
*/
- public static boolean remOp( String nick )
+ public boolean remOp( String nick )
{
return remFromGroup( opGroupName, nick );
}
@@ -154,17 +137,9 @@ public static boolean remOp( String nick )
* @param nick The nick to remove
* @return true iff an element was removed
*/
- private static boolean remFromGroup( String group, String nick )
+ private boolean remFromGroup( String group, String nick )
{
- Jedis jedis = pool.getResource();
- try
- {
- long reply = jedis.srem( keyOfGroupSetsPrefix + group, nick );
- return reply == 1;
- }
- finally
- {
- pool.returnResource( jedis );
- }
+ long reply = redisClient.srem( List.of( keyOfGroupSetsPrefix + group, nick ) ).toLong();
+ return reply == 1;
}
}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/KarmaDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/KarmaDB.java
new file mode 100644
index 00000000..1e9db635
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/KarmaDB.java
@@ -0,0 +1,184 @@
+/**
+ * Copyright (C) 2013 Oldterns
+ *
+ * This file may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+package com.oldterns.vilebot.database;
+
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@ApplicationScoped
+public class KarmaDB
+{
+ private static final String keyOfKarmaSortedSet = "noun-karma";
+
+ @Inject
+ RedisClient redisClient;
+
+ /**
+ * Change the karma of a noun by an integer.
+ *
+ * @param noun The noun to change the karma of
+ * @param mod The amount to change the karma by, may be negative.
+ */
+ public void modNounKarma( String noun, Number mod )
+ {
+ redisClient.zincrby( keyOfKarmaSortedSet, Long.toString( mod.longValue() ), noun );
+ }
+
+ /**
+ * Get the karma of a noun.
+ *
+ * @param noun The noun to query to karma of
+ * @return Integer iff the noun has a defined value, else null
+ */
+ public Optional getNounKarma( String noun )
+ {
+ return Optional.ofNullable( redisClient.zscore( keyOfKarmaSortedSet, noun ) ).map( Response::toLong );
+ }
+
+ /**
+ * Get the rank of a noun based on its karma.
+ *
+ * @param noun The noun to query the rank of
+ * @return Integer iff the noun has a defined value, else null
+ */
+ public Optional getNounRank( String noun )
+ {
+ return Optional.ofNullable( redisClient.zrevrank( keyOfKarmaSortedSet,
+ noun ) ).map( response -> response.toLong() + 1L );
+ }
+
+ /**
+ * Get the rank of a noun based on its karma, starting at most negative karma.
+ *
+ * @param noun The noun to query the reverse rank of
+ * @return Integer iff the noun has a defined value, else null
+ */
+ public Optional getNounRevRank( String noun )
+ {
+ return Optional.ofNullable( redisClient.zrank( keyOfKarmaSortedSet,
+ noun ) ).map( Response::toLong ).map( revRank -> revRank + 1 );
+ }
+
+ /**
+ * Get noun from a karma rank (Rank 1 is the member with the highest karma).
+ *
+ * @param rank The rank to get the noun of.
+ * @return String The noun iff the rank exists, else null.
+ */
+ public String getRankNoun( long rank )
+ {
+ Set nouns = getRankNouns( rank - 1, rank );
+
+ if ( nouns != null && nouns.iterator().hasNext() )
+ {
+ return nouns.iterator().next();
+ }
+ return null;
+ }
+
+ /**
+ * Get nouns from karma ranks.
+ *
+ * @param lower The lower rank to get the nouns of.
+ * @param upper The upper rank to get the nouns of.
+ * @return String The noun iff the rank exists, else null.
+ */
+ public Set getRankNouns( Long lower, Long upper )
+ {
+ Set nouns =
+ redisClient.zrevrange( List.of( keyOfKarmaSortedSet, lower.toString(),
+ upper.toString() ) ).stream().map( Response::toString ).collect( Collectors.toSet() );
+
+ if ( nouns.size() == 0 )
+ {
+ return null;
+ }
+
+ return nouns;
+ }
+
+ /**
+ * Get noun from a karma rank, starting with the lowest ranks (Rank 1 would be the member with the least karma).
+ *
+ * @param rank The reversed rank to get the noun of.
+ * @return String The noun iff the rank exists, else null.
+ */
+ public String getRevRankNoun( long rank )
+ {
+ Set nouns = getRevRankNouns( rank - 1, rank );
+
+ if ( nouns != null && nouns.iterator().hasNext() )
+ {
+ return nouns.iterator().next();
+ }
+ return null;
+ }
+
+ /**
+ * Get nouns from a karma rank, starting with the lowest ranks.
+ *
+ * @param lower The lower rank to get the nouns of.
+ * @param upper The upper rank to get the nouns of.
+ * @return String The noun iff the rank exists, else null.
+ */
+ public Set getRevRankNouns( Long lower, Long upper )
+ {
+ Set nouns =
+ redisClient.zrange( List.of( keyOfKarmaSortedSet, lower.toString(),
+ upper.toString() ) ).stream().map( Response::toString ).collect( Collectors.toSet() );
+
+ if ( nouns.size() == 0 )
+ {
+ return null;
+ }
+
+ return nouns;
+ }
+
+ /**
+ * Remove noun from the karma/rank set.
+ *
+ * @param noun The noun to remove, if it exists.
+ * @return true iff the noun existed before removing it.
+ */
+ public boolean remNoun( String noun )
+ {
+ Long existed = redisClient.zrem( List.of( keyOfKarmaSortedSet, noun ) ).toLong();
+
+ if ( existed == null || existed != 1 )
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public long getTotalKarma()
+ {
+ Set members =
+ redisClient.zrange( List.of( keyOfKarmaSortedSet, "0",
+ "-1" ) ).stream().map( Response::toString ).collect( Collectors.toSet() );
+ return sum( members );
+ }
+
+ private long sum( Set members )
+ {
+ long sum = 0;
+ for ( String member : members )
+ {
+ sum += redisClient.zscore( keyOfKarmaSortedSet, member ).toLong();
+ }
+ return sum;
+ }
+
+}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/LastSeenDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/LastSeenDB.java
new file mode 100644
index 00000000..11eb23f7
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/LastSeenDB.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2013 Oldterns
+ *
+ * This file may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+package com.oldterns.vilebot.database;
+
+import com.oldterns.vilebot.util.TimeService;
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Optional;
+
+@ApplicationScoped
+public class LastSeenDB
+{
+ private static final String keyOfLastSeenHash = "last-seen";
+
+ @Inject
+ RedisClient redisClient;
+
+ @Inject
+ TimeService timeService;
+
+ public Optional getLastSeenTime( String nick )
+ {
+ Response response = redisClient.hget( keyOfLastSeenHash, nick );
+ if ( response != null )
+ {
+ return Optional.of( response.toLong() );
+ }
+ else
+ {
+ return Optional.empty();
+ }
+ }
+
+ public void updateLastSeenTime( String nick )
+ {
+ Long milliTime = timeService.getCurrentTimeMills();
+ redisClient.hset( List.of( keyOfLastSeenHash, nick, milliTime.toString() ) );
+ }
+}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/LogDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/LogDB.java
new file mode 100644
index 00000000..79d6c4c5
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/LogDB.java
@@ -0,0 +1,45 @@
+package com.oldterns.vilebot.database;
+
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Created by eunderhi on 18/08/15.
+ */
+@ApplicationScoped
+public class LogDB
+{
+
+ public static String logKey = "chat-log";
+
+ private static final int MAX_LOG_SIZE = 100000;
+
+ @Inject
+ RedisClient redisClient;
+
+ public void addItem( String chatMessage )
+ {
+ if ( redisClient.strlen( logKey ).toLong() > MAX_LOG_SIZE )
+ {
+ redisClient.set( List.of( logKey, StringUtils.right( redisClient.get( logKey ).toString(),
+ MAX_LOG_SIZE - chatMessage.length() ) ) );
+ }
+ redisClient.append( logKey, chatMessage );
+ }
+
+ public String getLog()
+ {
+ return Optional.ofNullable( redisClient.get( logKey ) ).map( Response::toString ).orElse( "" );
+ }
+
+ public void deleteLog()
+ {
+ redisClient.del( List.of( logKey ) );
+ }
+}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/PasswordDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/PasswordDB.java
new file mode 100644
index 00000000..d1beff51
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/PasswordDB.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright (C) 2013 Oldterns
+ *
+ * This file may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+package com.oldterns.vilebot.database;
+
+import com.oldterns.vilebot.util.HMAC;
+import com.oldterns.vilebot.util.RandomProvider;
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.UUID;
+
+@ApplicationScoped
+public class PasswordDB
+{
+ private static final String keyOfPassHash = "password";
+
+ private static final String keyOfPassSaltsHash = "password-salts";
+
+ @Inject
+ RedisClient redisClient;
+
+ @Inject
+ RandomProvider randomProvider;
+
+ // Note, there is some overlap in terms between the crypto term "Secure Hash" and the Redis structure "Hash". Redis
+ // hashes are maps, though called hashes because of a common method of implementing them via a hash function. Except
+ // for keyOfPassHash and keyOfPassSaltsHash, every use of "hash" in this file refers to the cryptography term.
+
+ /**
+ * @return A long random string
+ */
+ private String generateSalt()
+ {
+ byte[] randomBytes = new byte[16];
+ randomProvider.getRandom().nextBytes( randomBytes );
+ return UUID.nameUUIDFromBytes( randomBytes ).toString();
+ }
+
+ private String getSalt( String username )
+ {
+ return redisClient.hget( keyOfPassSaltsHash, username ).toString();
+ }
+
+ private static String hash( String salt, String input )
+ {
+ return HMAC.generateHMAC( input, salt );
+ }
+
+ /**
+ * Checks the validity of a password for a user
+ *
+ * @param username user to check the password of
+ * @param password the password to check
+ * @return true iff the given password is valid
+ */
+ public boolean isValidPassword( String username, String password )
+ {
+ Response storedHash = redisClient.hget( keyOfPassHash, username );
+ if ( storedHash == null )
+ {
+ return false;
+ }
+ String hash = hash( password, getSalt( username ) );
+ return hash.equals( storedHash.toString() );
+ }
+
+ /**
+ * Remove a user and their password information, if they exist.
+ *
+ * @param username unique user name
+ */
+ public void removeUserPassword( String username )
+ {
+ redisClient.multi();
+ redisClient.hdel( List.of( keyOfPassHash, username ) );
+ redisClient.hdel( List.of( keyOfPassSaltsHash, username ) );
+ redisClient.exec();
+ }
+
+ /**
+ * Add or modify a user's password
+ *
+ * @param username unique user name
+ * @param password the user's password (will be hashed)
+ * @return true iff a new element was inserted
+ */
+ public boolean setUserPassword( String username, String password )
+ {
+ boolean newUser;
+ do
+ {
+ // Can't use intermediate results of a Redis transaction in that transaction, so watch the keys and do
+ // the query before opening the transaction. The transaction will fail on exec() call if the keys
+ // changed.
+ redisClient.watch( List.of( keyOfPassHash, keyOfPassSaltsHash ) );
+ boolean exists = redisClient.hexists( keyOfPassHash, username ).toBoolean();
+
+ redisClient.multi();
+ // Create a salt as well as the new password entry if the user is new
+ if ( !exists )
+ {
+ newUser = true;
+
+ String salt = generateSalt();
+ redisClient.hset( List.of( keyOfPassSaltsHash, username, salt ) );
+
+ String hash = hash( password, salt );
+ redisClient.hset( List.of( keyOfPassHash, username, hash ) );
+ }
+ else
+ {
+ // So, password cannot be changed once created?
+ newUser = false;
+ }
+ }
+ while ( redisClient.exec() == null );
+ return newUser;
+ }
+}
\ No newline at end of file
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/QuoteFactDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/QuoteFactDB.java
new file mode 100644
index 00000000..ad3a9d22
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/QuoteFactDB.java
@@ -0,0 +1,163 @@
+/**
+ * Copyright (C) 2013 Oldterns
+ *
+ * This file may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+package com.oldterns.vilebot.database;
+
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@ApplicationScoped
+public class QuoteFactDB
+{
+ private static final String keyOfQuoteSetsPrefix = "noun-quotes-";
+
+ private static final String keyOfFactSetsPrefix = "noun-facts-";
+
+ private static final int COUNT = 5;
+
+ @Inject
+ RedisClient redisClient;
+
+ /**
+ * Add a quote to the quote set of a noun.
+ *
+ * @param noun The noun to add a quote to
+ * @param quote The new quote
+ */
+ public void addQuote( String noun, String quote )
+ {
+ redisClient.sadd( List.of( keyOfQuoteSetsPrefix + noun, quote ) );
+ }
+
+ /**
+ * Get the quotes of a noun.
+ *
+ * @param noun The noun to get the quotes of
+ * @return The set of quotes for the noun
+ */
+ public Set getQuotes( String noun )
+ {
+ return redisClient.smembers( keyOfQuoteSetsPrefix
+ + noun ).stream().map( Response::toString ).collect( Collectors.toSet() );
+ }
+
+ /**
+ * Get a random quote from the set of quotes of a noun.
+ *
+ * @param noun The noun to get the quotes of
+ * @return A random quote for the noun
+ */
+ public String getRandomQuote( String noun )
+ {
+ return Optional.ofNullable( redisClient.srandmember( List.of( keyOfQuoteSetsPrefix
+ + noun ) ) ).map( Response::toString ).orElse( null );
+ }
+
+ /**
+ * Get the number of quotes associated with a noun.
+ *
+ * @param noun The noun to get the quotes of
+ * @return The number of quotes
+ */
+ public Long getQuotesLength( String noun )
+ {
+ return redisClient.scard( keyOfQuoteSetsPrefix + noun ).toLong();
+ }
+
+ /**
+ * Get 5 random quotes from the set of facts of a noun.
+ *
+ * @param noun The noun to get the quotes of
+ * @return A List of 5 random quotes for the noun
+ */
+ public List getRandomQuotes( String noun )
+ {
+ List randomQuotes = new LinkedList();
+
+ // TODO: This implementation is wrong; doesn't handle
+ // duplicate quotes or when quotes have less than 5 members
+ for ( int i = 0; i < COUNT; i++ )
+ {
+ String quote = getRandomQuote( noun );
+ randomQuotes.add( quote );
+ }
+ return randomQuotes;
+ }
+
+ /**
+ * Add a fact to the fact set of a noun.
+ *
+ * @param noun The noun to add the fact to
+ * @param quote The new quote
+ */
+ public void addFact( String noun, String quote )
+ {
+ redisClient.sadd( List.of( keyOfFactSetsPrefix + noun, quote ) );
+ }
+
+ /**
+ * Get the facts of a noun.
+ *
+ * @param noun The noun to get the facts of
+ * @return The set of facts for the noun
+ */
+ public Set getFacts( String noun )
+ {
+ return redisClient.smembers( keyOfFactSetsPrefix
+ + noun ).stream().map( Response::toString ).collect( Collectors.toSet() );
+ }
+
+ /**
+ * Get a random fact from the set of facts of a noun.
+ *
+ * @param noun The noun to get the facts of
+ * @return A random fact for the noun
+ */
+ public String getRandomFact( String noun )
+ {
+ return Optional.ofNullable( redisClient.srandmember( List.of( keyOfFactSetsPrefix
+ + noun ) ) ).map( Response::toString ).orElse( null );
+ }
+
+ /**
+ * Get the number of facts associated with a noun.
+ *
+ * @param noun The noun to get the facts of
+ * @return The number of facts
+ */
+ public Long getFactsLength( String noun )
+ {
+ return redisClient.scard( keyOfFactSetsPrefix + noun ).toLong();
+ }
+
+ /**
+ * Get 5 random facts from the set of facts of a noun.
+ *
+ * @param noun The noun to get the facts of
+ * @return A List of 5 random facts for the noun
+ */
+ public List getRandomFacts( String noun )
+ {
+ List randomFact = new LinkedList();
+
+ // TODO: This implementation is wrong; doesn't handle
+ // duplicate facts or when quotes have less than 5 members
+ for ( int i = 0; i < COUNT; i++ )
+ {
+ String quote = getRandomFact( noun );
+ randomFact.add( quote );
+ }
+ return randomFact;
+ }
+}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/database/UserlistDB.java b/vilebot/src/main/java/com/oldterns/vilebot/database/UserlistDB.java
new file mode 100644
index 00000000..5f2ab705
--- /dev/null
+++ b/vilebot/src/main/java/com/oldterns/vilebot/database/UserlistDB.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (C) 2013 Oldterns
+ *
+ * This file may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+package com.oldterns.vilebot.database;
+
+import io.quarkus.redis.client.RedisClient;
+import io.vertx.redis.client.Response;
+import org.jboss.logging.Logger;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@ApplicationScoped
+public class UserlistDB
+{
+ private static final String keyOfUserlistSetsPrefix = "userlist-";
+
+ @Inject
+ RedisClient redisClient;
+
+ /**
+ * Get the nicks in a userlist.
+ *
+ * @param listName The name of the list
+ * @return The set of nicks for the list
+ */
+ public Set getUsersIn( String listName )
+ {
+ return redisClient.smembers( keyOfUserlistSetsPrefix
+ + listName ).stream().map( Response::toString ).collect( Collectors.toSet() );
+ }
+
+ /**
+ * Add the given nicks to a userlist.
+ *
+ * @param listName The name of the list param The set of nicks for the list
+ * @param nicks The nicks to add
+ */
+ public void addUsersTo( String listName, Collection nicks )
+ {
+ List args = new ArrayList<>( nicks.size() + 1 );
+ args.add( keyOfUserlistSetsPrefix + listName );
+ args.addAll( nicks );
+ redisClient.sadd( args );
+ }
+
+ /**
+ * Remove the given nicks from a userlist.
+ *
+ * @param listName The name of the list param The set of nicks for the list
+ * @param nicks The nicks to remove
+ */
+ public void removeUsersFrom( String listName, Collection nicks )
+ {
+ List args = new ArrayList<>( nicks.size() + 1 );
+ args.add( keyOfUserlistSetsPrefix + listName );
+ args.addAll( nicks );
+ redisClient.srem( args );
+ }
+
+ /**
+ * Get the set of the available userlists.
+ *
+ * @return The set of userlists
+ */
+ public Set getLists()
+ {
+ // KEYS is O(n) operation, so it won't scale propery for large numbers of database keys.
+ // We're going to use it here however, as the number of keys in our database should never get large enough
+ // for this to matter, and the alternative of maintaining a set of userlists has the potential to be buggy.
+ Set rawlists = redisClient.keys( keyOfUserlistSetsPrefix
+ + "*" ).stream().map( Response::toString ).collect( Collectors.toSet() );
+ Set lists = new HashSet();
+
+ // Strip the prefix from each of the keys to isolate the list name
+ int cut = keyOfUserlistSetsPrefix.length();
+ for ( String rawlist : rawlists )
+ {
+ String list = rawlist.substring( cut );
+ lists.add( list );
+ }
+
+ return lists;
+ }
+}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/ChurchDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/ChurchDB.java
deleted file mode 100644
index 2696403d..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/ChurchDB.java
+++ /dev/null
@@ -1,242 +0,0 @@
-package com.oldterns.vilebot.db;
-
-import java.util.Set;
-
-import redis.clients.jedis.Jedis;
-
-/**
- * This class is effectively a duplicate of KarmaDB, used for church related karma
- */
-public class ChurchDB
- extends RedisDB
-{
- private static final String keyOfChurchDonorSortedSet = "church-donor-karma";
-
- private static final String keyOfChurchSortedSet = "church-karma";
-
- private static final String keyOfChurchDonorTitleSortedSet = "church-title-";
-
- public static boolean isTopDonor( String noun )
- {
- return getDonorRank( noun ) != null && getDonorRank( noun ) <= 4;
- }
-
- /**
- * Change the karma of a noun by an integer.
- *
- * @param noun The noun to change the karma of
- * @param mod The amount to change the karma by, may be negative.
- */
- public static void modDonorKarma( String noun, int mod )
- {
- Jedis jedis = pool.getResource();
- try
- {
- jedis.zincrby( keyOfChurchDonorSortedSet, mod, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Change the karma not affiliated with a donor
- */
- public static void modNonDonorKarma( int mod )
- {
- Jedis jedis = pool.getResource();
- try
- {
- jedis.zincrby( keyOfChurchSortedSet, mod, keyOfChurchSortedSet );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Change the title of noun to a string.
- *
- * @param noun The noun to change the karma of
- * @param newTitle The string to change the title to
- */
- public static void modDonorTitle( String noun, String newTitle )
- {
- Jedis jedis = pool.getResource();
- Long titleCount = jedis.scard( keyOfChurchDonorTitleSortedSet + noun );
- try
- {
- for ( Long i = 0L; i < titleCount; i++ )
- {
- jedis.spop( keyOfChurchDonorTitleSortedSet + noun );
- }
- jedis.sadd( keyOfChurchDonorTitleSortedSet + noun, newTitle );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get the karma of a noun.
- *
- * @param noun The noun to query to karma of
- * @return Integer iff the noun has a defined value, else null
- */
- public static Integer getDonorKarma( String noun )
- {
- Jedis jedis = pool.getResource();
- Double karma;
- try
- {
- karma = jedis.zscore( keyOfChurchDonorSortedSet, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- if ( karma == null )
- {
- return null;
- }
-
- return Long.valueOf( Math.round( karma ) ).intValue();
- }
-
- /**
- * Get the rank of a noun based on its karma.
- *
- * @param noun The noun to query the rank of
- * @return Integer iff the noun has a defined value, else null
- */
- public static Integer getDonorRank( String noun )
- {
- Jedis jedis = pool.getResource();
- Long rank;
- try
- {
- rank = jedis.zrevrank( keyOfChurchDonorSortedSet, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- if ( rank == null )
- {
- return null;
- }
-
- return rank.intValue() + 1;
- }
-
- /**
- * Get the title of a noun.
- *
- * @param noun The noun to query the rank of
- * @return String iff the noun has a defined value, else null
- */
- public static String getDonorTitle( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.srandmember( keyOfChurchDonorTitleSortedSet + noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get nouns from karma ranks.
- *
- * @param lower The lower rank to get the nouns of.
- * @param upper The upper rank to get the nouns of.
- * @return String The noun iff the rank exists, else null.
- */
- public static Set getDonorsByRanks( long lower, long upper )
- {
- Set nouns;
- Jedis jedis = pool.getResource();
- try
- {
- nouns = jedis.zrevrange( keyOfChurchDonorSortedSet, lower, upper );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- if ( nouns == null || nouns.size() == 0 )
- {
- return null;
- }
-
- return nouns;
- }
-
- public static boolean removeDonor( String noun )
- {
- Long existed;
- Jedis jedis = pool.getResource();
- try
- {
- existed = jedis.zrem( keyOfChurchDonorSortedSet, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- if ( existed == null || existed != 1 )
- {
- return false;
- }
-
- return true;
- }
-
- public static long getTotalDonations()
- {
- Jedis jedis = pool.getResource();
- long totalKarma;
- try
- {
- Set members = jedis.zrange( keyOfChurchDonorSortedSet, 0, -1 );
- totalKarma = sum( keyOfChurchDonorSortedSet, members, jedis );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- return totalKarma;
- }
-
- public static long getTotalNonDonations()
- {
- Jedis jedis = pool.getResource();
- long totalKarma;
- try
- {
- Set members = jedis.zrange( keyOfChurchSortedSet, 0, -1 );
- totalKarma = sum( keyOfChurchSortedSet, members, jedis );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- return totalKarma;
- }
-
- private static long sum( String set, Set members, Jedis jedis )
- {
- long sum = 0;
- for ( String member : members )
- {
- sum += jedis.zscore( set, member );
- }
- return sum;
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/ExcuseDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/ExcuseDB.java
deleted file mode 100644
index a0ee94ad..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/ExcuseDB.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.db;
-
-import redis.clients.jedis.Jedis;
-
-public class ExcuseDB
- extends RedisDB
-{
- private static final String keyOfExcuseSet = "excuses";
-
- public static void addExcuse( String excuse )
- {
- Jedis jedis = pool.getResource();
- try
- {
- jedis.sadd( keyOfExcuseSet, excuse );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- public static String getRandExcuse()
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.srandmember( keyOfExcuseSet );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/KarmaDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/KarmaDB.java
deleted file mode 100644
index 4694aceb..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/KarmaDB.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.db;
-
-import java.util.Set;
-
-import redis.clients.jedis.Jedis;
-
-public class KarmaDB
- extends RedisDB
-{
- private static final String keyOfKarmaSortedSet = "noun-karma";
-
- /**
- * Change the karma of a noun by an integer.
- *
- * @param noun The noun to change the karma of
- * @param mod The amount to change the karma by, may be negative.
- */
- public static void modNounKarma( String noun, int mod )
- {
- Jedis jedis = pool.getResource();
- try
- {
- jedis.zincrby( keyOfKarmaSortedSet, mod, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get the karma of a noun.
- *
- * @param noun The noun to query to karma of
- * @return Integer iff the noun has a defined value, else null
- */
- public static Integer getNounKarma( String noun )
- {
- Jedis jedis = pool.getResource();
- Double karma;
- try
- {
- karma = jedis.zscore( keyOfKarmaSortedSet, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
-
- if ( karma == null )
- {
- return null;
- }
-
- return Integer.valueOf( Long.valueOf( Math.round( karma ) ).intValue() );
- }
-
- /**
- * Get the rank of a noun based on its karma.
- *
- * @param noun The noun to query the rank of
- * @return Integer iff the noun has a defined value, else null
- */
- public static Integer getNounRank( String noun )
- {
- Jedis jedis = pool.getResource();
- Long rank;
- try
- {
- rank = jedis.zrevrank( keyOfKarmaSortedSet, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
-
- if ( rank == null )
- {
- return null;
- }
-
- return Integer.valueOf( rank.intValue() + 1 );
- }
-
- /**
- * Get the rank of a noun based on its karma, starting at most negative karma.
- *
- * @param noun The noun to query the reverse rank of
- * @return Integer iff the noun has a defined value, else null
- */
- public static Integer getNounRevRank( String noun )
- {
- Jedis jedis = pool.getResource();
- Long rank;
- try
- {
- rank = jedis.zrank( keyOfKarmaSortedSet, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
-
- if ( rank == null )
- {
- return null;
- }
-
- return Integer.valueOf( rank.intValue() + 1 );
- }
-
- /**
- * Get noun from a karma rank (Rank 1 is the member with the highest karma).
- *
- * @param rank The rank to get the noun of.
- * @return String The noun iff the rank exists, else null.
- */
- public static String getRankNoun( long rank )
- {
- Set nouns = getRankNouns( rank - 1, rank );
-
- if ( nouns != null && nouns.iterator().hasNext() )
- {
- return nouns.iterator().next();
- }
- return null;
- }
-
- /**
- * Get nouns from karma ranks.
- *
- * @param lower The lower rank to get the nouns of.
- * @param upper The upper rank to get the nouns of.
- * @return String The noun iff the rank exists, else null.
- */
- public static Set getRankNouns( long lower, long upper )
- {
- Set nouns;
-
- Jedis jedis = pool.getResource();
- try
- {
- nouns = jedis.zrevrange( keyOfKarmaSortedSet, lower, upper );
- }
- finally
- {
- pool.returnResource( jedis );
- }
-
- if ( nouns == null || nouns.size() == 0 )
- {
- return null;
- }
-
- return nouns;
- }
-
- /**
- * Get noun from a karma rank, starting with the lowest ranks (Rank 1 would be the member with the least karma).
- *
- * @param rank The reversed rank to get the noun of.
- * @return String The noun iff the rank exists, else null.
- */
- public static String getRevRankNoun( long rank )
- {
- Set nouns = getRevRankNouns( rank - 1, rank );
-
- if ( nouns != null && nouns.iterator().hasNext() )
- {
- return nouns.iterator().next();
- }
- return null;
- }
-
- /**
- * Get nouns from a karma rank, starting with the lowest ranks.
- *
- * @param lower The lower rank to get the nouns of.
- * @param upper The upper rank to get the nouns of.
- * @return String The noun iff the rank exists, else null.
- */
- public static Set getRevRankNouns( long lower, long upper )
- {
- Set nouns;
-
- Jedis jedis = pool.getResource();
- try
- {
- nouns = jedis.zrange( keyOfKarmaSortedSet, lower, upper );
- }
- finally
- {
- pool.returnResource( jedis );
- }
-
- if ( nouns == null || nouns.size() == 0 )
- {
- return null;
- }
-
- return nouns;
- }
-
- /**
- * Remove noun from the karma/rank set.
- *
- * @param noun The noun to remove, if it exists.
- * @return true iff the noun existed before removing it.
- */
- public static boolean remNoun( String noun )
- {
- Long existed;
-
- Jedis jedis = pool.getResource();
- try
- {
- existed = jedis.zrem( keyOfKarmaSortedSet, noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
-
- if ( existed == null || existed != 1 )
- {
- return false;
- }
- return true;
- }
-
- public static long getTotalKarma()
- {
- Jedis jedis = pool.getResource();
- long totalKarma;
- try
- {
- Set members = jedis.zrange( keyOfKarmaSortedSet, 0, -1 );
- totalKarma = sum( members, jedis );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- return totalKarma;
- }
-
- private static long sum( Set members, Jedis jedis )
- {
- long sum = 0;
- for ( String member : members )
- {
- sum += jedis.zscore( keyOfKarmaSortedSet, member );
- }
- return sum;
- }
-
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/LastSeenDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/LastSeenDB.java
deleted file mode 100644
index df6505e5..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/LastSeenDB.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.db;
-
-import redis.clients.jedis.Jedis;
-
-public class LastSeenDB
- extends RedisDB
-{
- private static final String keyOfLastSeenHash = "last-seen";
-
- public static long getLastSeenTime( String nick )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return Long.valueOf( jedis.hget( keyOfLastSeenHash, nick ) );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- public static void updateLastSeenTime( String nick )
- {
- Jedis jedis = pool.getResource();
- try
- {
- Long milliTime = System.currentTimeMillis();
- jedis.hset( keyOfLastSeenHash, nick, milliTime.toString() );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/LogDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/LogDB.java
deleted file mode 100644
index abec87c1..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/LogDB.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.oldterns.vilebot.db;
-
-import org.apache.commons.lang3.StringUtils;
-import redis.clients.jedis.Jedis;
-
-/**
- * Created by eunderhi on 18/08/15.
- */
-public class LogDB
- extends RedisDB
-{
-
- public static String logKey = "chat-log";
-
- private static final int MAX_LOG_SIZE = 100000;
-
- public static void addItem( String chatMessage )
- {
- Jedis jedis = pool.getResource();
- try
- {
- if ( jedis.strlen( logKey ) > MAX_LOG_SIZE )
- {
- jedis.set( logKey, StringUtils.right( jedis.get( logKey ), MAX_LOG_SIZE - chatMessage.length() ) );
- }
- jedis.append( logKey, chatMessage );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- public static String getLog()
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.get( logKey );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- public static void deleteLog()
- {
- Jedis jedis = pool.getResource();
- try
- {
- jedis.del( logKey );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/PasswordDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/PasswordDB.java
deleted file mode 100644
index c2f115f8..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/PasswordDB.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.db;
-
-import java.util.UUID;
-
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.Transaction;
-
-import com.oldterns.vilebot.util.HMAC;
-
-public class PasswordDB
- extends RedisDB
-{
- private static final String keyOfPassHash = "password";
-
- private static final String keyOfPassSaltsHash = "password-salts";
-
- // Note, there is some overlap in terms between the crypto term "Secure Hash" and the Redis structure "Hash". Redis
- // hashes are maps, though called hashes because of a common method of implementing them via a hash function. Except
- // for keyOfPassHash and keyOfPassSaltsHash, every use of "hash" in this file refers to the cryptography term.
-
- /**
- * @return A long random string
- */
- private static String generateSalt()
- {
- return UUID.randomUUID().toString();
- }
-
- private static String getSalt( String username )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.hget( keyOfPassSaltsHash, username );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- private static String hash( String salt, String input )
- {
- return HMAC.generateHMAC( input, salt );
- }
-
- /**
- * Checks the validity of a password for a user
- *
- * @param username user to check the password of
- * @param password the password to check
- * @return true iff the given password is valid
- */
- public static boolean isValidPassword( String username, String password )
- {
- String storedHash;
-
- Jedis jedis = pool.getResource();
- try
- {
- storedHash = jedis.hget( keyOfPassHash, username );
- }
- finally
- {
- pool.returnResource( jedis );
- }
-
- String hash = hash( password, getSalt( username ) );
-
- return hash.equals( storedHash );
- }
-
- /**
- * Remove a user and their password information, if they exist.
- *
- * @param username unique user name
- */
- public static void remUserPassword( String username )
- {
- Jedis jedis = pool.getResource();
- try
- {
- Transaction trans = jedis.multi();
- trans.hdel( keyOfPassHash, username );
- trans.hdel( keyOfPassSaltsHash, username );
- trans.exec();
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Add or modify a user's password
- *
- * @param username unique user name
- * @param password the user's password (will be hashed)
- * @return true iff a new element was inserted
- */
- public static boolean setUserPassword( String username, String password )
- {
- Jedis jedis = pool.getResource();
- try
- {
- boolean newUser;
-
- Transaction trans;
- do
- {
- // Can't use intermediate results of a Redis transaction in that transaction, so watch the keys and do
- // the query before opening the transaction. The transaction will fail on exec() call if the keys
- // changed.
- jedis.watch( keyOfPassHash, keyOfPassSaltsHash );
- boolean exists = jedis.hexists( keyOfPassHash, username );
-
- trans = jedis.multi();
- // Create a salt as well as the new password entry if the user is new
- if ( !exists )
- {
- newUser = true;
-
- String salt = generateSalt();
- trans.hset( keyOfPassSaltsHash, username, salt );
-
- String hash = hash( password, salt );
- trans.hset( keyOfPassHash, username, hash );
- }
- else
- newUser = false;
- }
- while ( trans.exec() == null );
-
- return newUser;
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/QuoteFactDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/QuoteFactDB.java
deleted file mode 100644
index abb83ea4..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/QuoteFactDB.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.db;
-
-import java.util.Set;
-import java.util.LinkedList;
-import java.util.List;
-
-import redis.clients.jedis.Jedis;
-
-public class QuoteFactDB
- extends RedisDB
-{
- private static final String keyOfQuoteSetsPrefix = "noun-quotes-";
-
- private static final String keyOfFactSetsPrefix = "noun-facts-";
-
- private static final int COUNT = 5;
-
- /**
- * Add a quote to the quote set of a noun.
- *
- * @param noun The noun to add a quote to
- * @param quote The new quote
- */
- public static void addQuote( String noun, String quote )
- {
- Jedis jedis = pool.getResource();
- try
- {
- jedis.sadd( keyOfQuoteSetsPrefix + noun, quote );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get the quotes of a noun.
- *
- * @param noun The noun to get the quotes of
- * @return The set of quotes for the noun
- */
- public static Set getQuotes( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.smembers( keyOfQuoteSetsPrefix + noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get a random quote from the set of quotes of a noun.
- *
- * @param noun The noun to get the quotes of
- * @return A random quote for the noun
- */
- public static String getRandQuote( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.srandmember( keyOfQuoteSetsPrefix + noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get the number of quotes associated with a noun.
- *
- * @param noun The noun to get the quotes of
- * @return The number of quotes
- */
- public static Long getQuotesLength( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.scard( keyOfQuoteSetsPrefix + noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get 5 random quotes from the set of facts of a noun.
- *
- * @param noun The noun to get the quotes of
- * @return A List of 5 random quotes for the noun
- */
- public static List getRandQuotes( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- List randomQuotes = new LinkedList();
- for ( int i = 0; i < COUNT; i++ )
- {
- String quote = getRandQuote( noun );
- randomQuotes.add( quote );
- }
- return randomQuotes;
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Add a fact to the fact set of a noun.
- *
- * @param noun The noun to add the fact to
- * @param quote The new quote
- */
- public static void addFact( String noun, String fact )
- {
- Jedis jedis = pool.getResource();
- try
- {
- jedis.sadd( keyOfFactSetsPrefix + noun, fact );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get the facts of a noun.
- *
- * @param noun The noun to get the facts of
- * @return The set of facts for the noun
- */
- public static Set getFacts( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.smembers( keyOfFactSetsPrefix + noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get a random fact from the set of facts of a noun.
- *
- * @param noun The noun to get the facts of
- * @return A random fact for the noun
- */
- public static String getRandFact( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.srandmember( keyOfFactSetsPrefix + noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get the number of facts associated with a noun.
- *
- * @param noun The noun to get the facts of
- * @return The number of facts
- */
- public static Long getFactsLength( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.scard( keyOfFactSetsPrefix + noun );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get 5 random facts from the set of facts of a noun.
- *
- * @param noun The noun to get the facts of
- * @return A List of 5 random facts for the noun
- */
- public static List getRandFacts( String noun )
- {
- Jedis jedis = pool.getResource();
- try
- {
- List randomFacts = new LinkedList();
- for ( int i = 0; i < COUNT; i++ )
- {
- String fact = getRandFact( noun );
- randomFacts.add( fact );
- }
- return randomFacts;
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/RedisDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/RedisDB.java
deleted file mode 100644
index 3f35f58d..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/RedisDB.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.db;
-
-import redis.clients.jedis.JedisPool;
-
-import com.oldterns.vilebot.Vilebot;
-
-/**
- * Interact with Redis database via Jedis. See http://redis.io/commands for info about the command methods.
- */
-public abstract class RedisDB
-{
- protected static final JedisPool pool = Vilebot.getPool();
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/db/UserlistDB.java b/vilebot/src/main/java/com/oldterns/vilebot/db/UserlistDB.java
deleted file mode 100644
index 1ebd1ac0..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/db/UserlistDB.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.db;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-import redis.clients.jedis.Jedis;
-
-public class UserlistDB
- extends RedisDB
-{
- private static final String keyOfUserlistSetsPrefix = "userlist-";
-
- /**
- * Get the nicks in a userlist.
- *
- * @param noun The name of the list
- * @return The set of nicks for the list
- */
- public static Set getUsersIn( String listName )
- {
- Jedis jedis = pool.getResource();
- try
- {
- return jedis.smembers( keyOfUserlistSetsPrefix + listName );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Add the given nicks to a userlist.
- *
- * @param noun The name of the list param The set of nicks for the list
- * @param nicks The nicks to add
- */
- public static void addUsersTo( String listName, Collection nicks )
- {
- Jedis jedis = pool.getResource();
- try
- {
- String[] nicksArray = new String[nicks.size()];
- nicks.toArray( nicksArray );
- jedis.sadd( keyOfUserlistSetsPrefix + listName, nicksArray );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Remove the given nicks from a userlist.
- *
- * @param noun The name of the list param The set of nicks for the list
- * @param nicks The nicks to remove
- */
- public static void removeUsersFrom( String listName, Collection nicks )
- {
- Jedis jedis = pool.getResource();
- try
- {
- String[] nicksArray = new String[nicks.size()];
- nicks.toArray( nicksArray );
- jedis.srem( keyOfUserlistSetsPrefix + listName, nicksArray );
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-
- /**
- * Get the set of the available userlists.
- *
- * @return The set of userlists
- */
- public static Set getLists()
- {
- Jedis jedis = pool.getResource();
- try
- {
- // KEYS is O(n) operation, so it won't scale propery for large numbers of database keys.
- // We're going to use it here however, as the number of keys in our database should never get large enough
- // for this to matter, and the alternative of maintaining a set of userlists has the potential to be buggy.
- Set rawlists = jedis.keys( keyOfUserlistSetsPrefix + "*" );
-
- Set lists = new HashSet();
-
- // Strip the prefix from each of the keys to isolate the list name
- int cut = keyOfUserlistSetsPrefix.length();
- for ( String rawlist : rawlists )
- {
- String list = rawlist.substring( cut );
- lists.add( list );
- }
-
- return lists;
- }
- finally
- {
- pool.returnResource( jedis );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/AdminManagement.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/AdminManagement.java
deleted file mode 100644
index 97e38a32..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/AdminManagement.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.oldterns.vilebot.handlers.admin;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.db.PasswordDB;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class AdminManagement
- extends ListenerAdapter
-{
- private static final Pattern adminSetPattern = Pattern.compile( "!admin setadmin (\\S+) (\\S+)" );
-
- private static final Pattern adminRemPattern = Pattern.compile( "!admin remadmin (\\S+)" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher setMatcher = adminSetPattern.matcher( text );
- Matcher remMatcher = adminRemPattern.matcher( text );
- if ( setMatcher.matches() )
- set( event, setMatcher );
- if ( remMatcher.matches() )
- rem( event, remMatcher );
- }
-
- private void set( GenericMessageEvent event, Matcher matcher )
- {
- String editedAdminNick = matcher.group( 1 );
- String password = matcher.group( 2 );
-
- // Note that an empty admin group will allow a setadmin command to succeed (self-bootstrapping)
-
- String username = Sessions.getSession( event.getUser().getNick() );
- if ( GroupDB.noAdmins() || GroupDB.isAdmin( username ) )
- {
- if ( PasswordDB.setUserPassword( editedAdminNick, password ) )
- {
- GroupDB.addAdmin( editedAdminNick );
- }
- event.respondWith( "Added/modified admin " + editedAdminNick );
- }
- // }
- }
-
- // @Handler
- private void rem( GenericMessageEvent event, Matcher matcher )
- {
- String editedAdminNick = matcher.group( 1 );
-
- String username = Sessions.getSession( event.getUser().getNick() );
- if ( GroupDB.isAdmin( username ) )
- {
- if ( GroupDB.remAdmin( editedAdminNick ) )
- {
- PasswordDB.remUserPassword( editedAdminNick );
- }
- event.respondWith( "Removed admin " + editedAdminNick );
- }
- }
- // }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/AdminPing.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/AdminPing.java
deleted file mode 100644
index e71bd302..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/AdminPing.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.oldterns.vilebot.handlers.admin;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.events.PrivateMessageEvent;
-import org.pircbotx.output.OutputIRC;
-
-public class AdminPing
- extends ListenerAdapter
-{
-
- /**
- * Do I have admin access?
- */
-
- @Override
- public void onPrivateMessage( final PrivateMessageEvent event )
- {
- String replyTarget = event.getUser().getNick();
- OutputIRC outputQ = event.getBot().send();
- String text = event.getMessage();
- String sender = event.getUser().getNick();
- adminPing( outputQ, replyTarget, text, sender );
- }
-
- @Override
- public void onMessage( final MessageEvent event )
- {
- String replyTarget = event.getChannel().getName();
- OutputIRC outputQ = event.getBot().send();
- String text = event.getMessage();
- String sender = event.getUser().getNick();
- adminPing( outputQ, replyTarget, text, sender );
- }
-
- private void adminPing( OutputIRC outputQ, String replyTarget, String text, String sender )
- {
- if ( "!admin ping".equals( text ) )
- {
- String username = Sessions.getSession( sender );
- if ( GroupDB.isAdmin( username ) )
- {
- outputQ.message( replyTarget, "You have an active admin session" );
- }
- else
- {
- outputQ.message( replyTarget, "You do not have an active admin session" );
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Auth.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Auth.java
deleted file mode 100644
index 36f93e88..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Auth.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.oldterns.vilebot.handlers.admin;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.db.PasswordDB;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Auth
- extends ListenerAdapter
-{
- private static final Pattern authPattern = Pattern.compile( "!admin auth (\\S+) (\\S+)" );
-
- private static final long sessionLength = TimeUnit.MINUTES.toMillis( 5 );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = authPattern.matcher( text );
- String sender = event.getUser().getNick();
-
- if ( matcher.matches() )
- {
- String username = BaseNick.toBaseNick( matcher.group( 1 ) );
- String password = matcher.group( 2 );
-
- if ( GroupDB.isAdmin( username ) && PasswordDB.isValidPassword( username, password ) )
- {
- Sessions.addSession( sender, username, sessionLength );
- event.respondWith( "Authentication successful. Session active for "
- + TimeUnit.MILLISECONDS.toMinutes( sessionLength ) + " minutes." );
- }
- else
- {
- event.respondWith( "Authentication failed" );
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/GetLog.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/GetLog.java
deleted file mode 100644
index fe3e37b6..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/GetLog.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.oldterns.vilebot.handlers.admin;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.db.LogDB;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.regex.Pattern;
-
-public class GetLog
- extends ListenerAdapter
-{
-
- private static final Pattern showLog = Pattern.compile( "!admin showLog$" );
-
- private static final Pattern deleteLog = Pattern.compile( "!admin deleteLog$" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- String sender = event.getUser().getNick();
- String username = Sessions.getSession( sender );
-
- boolean showLogMatches = showLog.matcher( text ).matches();
- boolean deleteLogMatches = deleteLog.matcher( text ).matches();
-
- if ( GroupDB.isAdmin( username ) )
- {
- if ( showLogMatches )
- {
- event.respondWith( "Getting log..." );
- event.respondWith( LogDB.getLog() );
- }
- else if ( deleteLogMatches )
- {
- LogDB.deleteLog();
- event.respondWith( "Log deleted" );
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Help.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Help.java
deleted file mode 100644
index a50f99ca..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Help.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.oldterns.vilebot.handlers.admin;
-
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Help
- extends ListenerAdapter
-{
- private static final Pattern helpPattern = Pattern.compile( "!admin help" );
-
- private static final String helpMessage = generateHelpMessage();
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = helpPattern.matcher( text );
-
- if ( matcher.matches() )
- {
- event.respondPrivateMessage( helpMessage );
- }
- }
-
- private static String generateHelpMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "Available Commands:" );
-
- sb.append( " { !admin help }" );
- sb.append( " { !admin ping }" );
- sb.append( " { !admin auth }" );
- sb.append( " { !admin quit }" );
- sb.append( " { !admin nick }" );
- sb.append( " { !admin op }" );
- sb.append( " { !admin unrank }" );
- sb.append( " { !admin unop }" );
- sb.append( " { !admin setadmin }" );
- sb.append( " { !admin remadmin }" );
-
- return sb.toString();
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/NickChange.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/NickChange.java
deleted file mode 100644
index 5d5aaa11..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/NickChange.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- Copyright (C) 2013 Oldterns
-
- This file may be modified and distributed under the terms
- of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.admin;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class NickChange
- extends ListenerAdapter
-{
- private static final Pattern nickChangePattern = Pattern.compile( "!admin nick ([a-zA-Z][a-zA-Z0-9-_|]+)" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = nickChangePattern.matcher( text );
- String sender = event.getUser().getNick();
-
- if ( matcher.matches() )
- {
- String username = Sessions.getSession( sender );
- if ( GroupDB.isAdmin( username ) )
- {
- String newNick = matcher.group( 1 );
- event.getBot().send().changeNick( newNick );
- BaseNick.addBotNick( newNick );
-
- event.respondWith( "Nick changed" );
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Ops.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Ops.java
deleted file mode 100644
index eb92ab77..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Ops.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- Copyright (C) 2013 Oldterns
-
- This file may be modified and distributed under the terms
- of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.admin;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Ops
- extends ListenerAdapter
-{
- private static final Pattern nickPattern = Pattern.compile( "(\\S+)" );
-
- private static final Pattern addRemOpPattern =
- Pattern.compile( "!admin (un|)op ((?:" + nickPattern + "(?:, +| +|$))+)" );
-
- @Override
- public void onGenericMessage( GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = addRemOpPattern.matcher( text );
- String sender = event.getUser().getNick();
-
- if ( matcher.matches() )
- {
- String username = Sessions.getSession( sender );
- if ( GroupDB.isAdmin( username ) )
- {
- String mode = BaseNick.toBaseNick( matcher.group( 1 ) );
- String nickBlob = matcher.group( 2 );
-
- List nicks = new LinkedList<>();
- Matcher nickMatcher = nickPattern.matcher( nickBlob );
- while ( nickMatcher.find() )
- {
- nicks.add( BaseNick.toBaseNick( nickMatcher.group( 1 ) ) );
- }
-
- StringBuilder successNicks = new StringBuilder();
- StringBuilder failureNicks = new StringBuilder();
-
- if ( "un".equals( mode ) )
- {
- for ( String nick : nicks )
- {
- StringBuilder selectedSB;
- if ( GroupDB.remOp( nick ) )
- selectedSB = successNicks;
- else
- selectedSB = failureNicks;
-
- selectedSB.append( nick );
- selectedSB.append( " " );
- }
-
- if ( successNicks.length() > 0 )
- event.respondWith( "Removed " + successNicks.toString() + "from operator group" );
- if ( failureNicks.length() > 0 )
- event.respondWith( failureNicks.toString() + "was/were not in the operator group" );
- }
- else
- {
- for ( String nick : nicks )
- {
- StringBuilder selectedSB;
- if ( GroupDB.addOp( nick ) )
- selectedSB = successNicks;
- else
- selectedSB = failureNicks;
-
- selectedSB.append( nick );
- selectedSB.append( " " );
- }
-
- if ( successNicks.length() > 0 )
- event.respondWith( "Added " + successNicks.toString() + "to operator group" );
- if ( failureNicks.length() > 0 )
- event.respondWith( failureNicks.toString() + "was/were already in the operator group" );
- }
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Quit.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Quit.java
deleted file mode 100644
index 639d0c62..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/admin/Quit.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.admin;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-public class Quit
- extends ListenerAdapter
-{
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- String nick = event.getUser().getNick();
-
- if ( "!admin quit".equals( text ) )
- {
- String username = Sessions.getSession( nick );
- if ( GroupDB.isAdmin( username ) )
- {
- event.getBot().send().quitServer();
-
- System.exit( 0 );
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Ascii.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Ascii.java
deleted file mode 100644
index 15cf6853..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Ascii.java
+++ /dev/null
@@ -1,191 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.github.lalyos.jfiglet.FigletFont;
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.util.LimitCommand;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Ascii
- extends ListenerAdapter
-{
-
- private static final Pattern asciiPattern = Pattern.compile( "^!(ascii)\\s(.+)$" );
-
- private static final Pattern asciiFontsPattern = Pattern.compile( "!asciifonts" );
-
- private static final int MAX_CHARS_PER_LINE = 20;
-
- private static final int MAX_CHARS = MAX_CHARS_PER_LINE * 3; // max 3 lines
-
- private static final String fontFile = "./files/fonts/%s.flf";
-
- private static final String fontFileDir = "./files/fonts/";
-
- private static final List availableFonts = getAvailableFonts();
-
- public LimitCommand limitCommand = new LimitCommand();
-
- private static final String RESTRICTED_CHANNEL = Vilebot.getConfig().get( "ircChannel1" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher asciiMatch = asciiPattern.matcher( text );
- Matcher asciifontsMatch = asciiFontsPattern.matcher( text );
-
- if ( asciiMatch.matches() )
- ascii( event, asciiMatch );
- if ( asciifontsMatch.matches() )
- asciifonts( event );
- }
-
- private void ascii( GenericMessageEvent event, Matcher matcher )
- {
- if ( event instanceof MessageEvent
- && ( (MessageEvent) event ).getChannel().getName().equals( RESTRICTED_CHANNEL ) )
- {
- String isLimit = limitCommand.addUse( event.getUser().getNick() );
- if ( isLimit.isEmpty() )
- {
- runAscii( event, matcher );
- }
- else
- {
- event.respondWith( isLimit );
- }
- }
- else
- {
- runAscii( event, matcher );
- }
- }
-
- private void asciifonts( GenericMessageEvent event )
- {
- StringBuilder sb = new StringBuilder();
- sb.append( "Available fonts for !ascii:\n" );
- for ( int i = 0; i < availableFonts.size(); i++ )
- {
- sb.append( String.format( "%20s ", availableFonts.get( i ) ) );
- if ( ( ( i + 1 ) % 5 ) == 0 )
- {
- sb.append( "\n" );
- }
- }
- for ( String line : sb.toString().split( "\n" ) )
- {
- event.respondPrivateMessage( line );
- }
- }
-
- private void runAscii( GenericMessageEvent event, Matcher asciiMatch )
- {
- // check if first word is a font name
- String font = asciiMatch.group( 2 ).split( " ", 2 )[0];
- String asciiArt;
- if ( availableFonts.contains( font ) )
- {
- String message = asciiMatch.group( 2 ).split( " ", 2 )[1];
- asciiArt = getAsciiArt( message, font );
- }
- else
- {
- String message = asciiMatch.group( 2 );
- asciiArt = getAsciiArt( message );
- }
- for ( String line : asciiArt.split( "\n" ) )
- {
- event.respondWith( line );
- }
- }
-
- private static List getAvailableFonts()
- {
- List fonts = new ArrayList<>();
- for ( File flf : Objects.requireNonNull( new File( fontFileDir ).listFiles() ) )
- {
- if ( flf.isFile() )
- {
- String filename = flf.getName();
- fonts.add( filename.substring( 0, filename.length() - 4 ) );
- }
- }
- Collections.sort( fonts );
- return fonts;
- }
-
- private static String getAsciiArt( String text )
- {
- try
- {
- StringBuilder sb = new StringBuilder();
- String[] lines = splitMessage( text );
- for ( String line : lines )
- {
- sb.append( FigletFont.convertOneLine( line ) );
- }
- return sb.toString();
- }
- catch ( IOException e )
- {
- e.printStackTrace();
- return "Could not get ASCII art";
- }
- }
-
- private String getAsciiArt( String text, String font )
- {
- try
- {
- StringBuilder sb = new StringBuilder();
- String[] lines = splitMessage( text );
- for ( String line : lines )
- {
- sb.append( FigletFont.convertOneLine( String.format( fontFile, font ), line ) );
- }
- return sb.toString();
- }
- catch ( IOException e )
- {
- e.printStackTrace();
- return "Could not get ASCII art";
- }
- }
-
- private static String[] splitMessage( String message )
- {
- List lines = new ArrayList<>();
- if ( message.length() > MAX_CHARS )
- {
- message = message.substring( 0, MAX_CHARS );
- }
- int len = message.length();
- for ( int i = 0; i < len; i += MAX_CHARS_PER_LINE )
- {
- int substrEndIdx = Math.min( len, i + MAX_CHARS_PER_LINE );
- if ( i + MAX_CHARS_PER_LINE < len && Character.isLetter( message.charAt( substrEndIdx - 1 ) )
- && Character.isLetter( message.charAt( substrEndIdx ) ) )
- {
- lines.add( message.substring( i, substrEndIdx ) + "-" );
- }
- else
- {
- lines.add( message.substring( i, substrEndIdx ) );
- }
- }
- return lines.toArray( new String[0] );
- }
-
-}
\ No newline at end of file
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/ChatLogger.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/ChatLogger.java
deleted file mode 100644
index 96512346..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/ChatLogger.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.db.LogDB;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-
-/**
- * Created by eunderhi on 18/08/15.
- */
-
-public class ChatLogger
- extends ListenerAdapter
-{
- @Override
- public void onMessage( final MessageEvent event )
- {
- String logChannel = Vilebot.getConfig().get( "markovChannel" );
- String channel = event.getChannel().getName();
- String text = event.getMessage();
-
- boolean shouldLog = channel.equals( logChannel ) && !text.startsWith( "!" );
-
- if ( shouldLog )
- {
- LogDB.addItem( text + "\n" );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Church.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Church.java
deleted file mode 100644
index 59b05b2a..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Church.java
+++ /dev/null
@@ -1,378 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import java.util.Objects;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.oldterns.vilebot.db.KarmaDB;
-import com.oldterns.vilebot.db.ChurchDB;
-import com.oldterns.vilebot.util.BaseNick;
-
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-public class Church
- extends ListenerAdapter
-{
- private static final Pattern nounPattern = Pattern.compile( "\\S+" );
-
- private static final Pattern donatePattern = Pattern.compile( "^!donate (-?[0-9]+)\\s*" );
-
- private static final Pattern churchTotalPattern = Pattern.compile( "^!churchtotal" );
-
- private static final Pattern topDonorsPattern = Pattern.compile( "^!topdonors" );
-
- private static final Pattern setTitlesPattern = Pattern.compile( "^!settitle (.+)$" );
-
- private static final Pattern inquisitPattern = Pattern.compile( "^!inquisit (" + nounPattern + ")\\s*$" );
-
- private static final Pattern topDonorsAyePattern = Pattern.compile( "^!aye" );
-
- private static final Pattern topDonorsNayPattern = Pattern.compile( "^!nay" );
-
- private static final long TIMEOUT = 30000L;
-
- private static ExecutorService timer = Executors.newScheduledThreadPool( 1 );
-
- private static VoteEvent currentVote = null;
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
-
- Matcher donateToChurchMatcher = donatePattern.matcher( text );
- Matcher viewChurchTotalMatcher = churchTotalPattern.matcher( text );
- Matcher viewTopDonorsMatcher = topDonorsPattern.matcher( text );
- Matcher setTitleMatcher = setTitlesPattern.matcher( text );
- Matcher inquisitMatcher = inquisitPattern.matcher( text );
- Matcher matcherAye = topDonorsAyePattern.matcher( event.getMessage() );
- Matcher matcherNay = topDonorsNayPattern.matcher( event.getMessage() );
-
- if ( donateToChurchMatcher.matches() )
- donateToChurch( event, donateToChurchMatcher );
- if ( viewChurchTotalMatcher.matches() )
- viewChurchTotal( event );
- if ( viewTopDonorsMatcher.matches() )
- viewTopDonors( event );
- if ( setTitleMatcher.matches() )
- setTitle( event, setTitleMatcher );
- if ( inquisitMatcher.matches() )
- inquisit( event, inquisitMatcher );
- if ( matcherAye.matches() || matcherNay.matches() )
- inquisitDecision( event, matcherAye, matcherNay );
- }
-
- private void donateToChurch( GenericMessageEvent event, Matcher matcher )
- {
- if ( currentVote != null )
- {
- event.respondWith( "There is an ongoing vote, you cannot donate at this time." );
- return;
- }
-
- String rawDonationAmount = matcher.group( 1 );
- String donor = BaseNick.toBaseNick( event.getUser().getNick() );
- Integer donationAmount = Integer.parseInt( rawDonationAmount );
- Integer donorKarma = KarmaDB.getNounKarma( donor );
- donorKarma = donorKarma == null ? 0 : donorKarma;
- if ( donationAmount <= 0 )
- {
- event.respondWith( "You cannot donate a non-positive number." );
- }
- else if ( donorKarma <= 0 || donorKarma < donationAmount )
- {
- event.respondWith( "You have insufficient karma to donate." );
- }
- else
- {
- ChurchDB.modDonorKarma( donor, donationAmount );
- KarmaDB.modNounKarma( donor, -1 * donationAmount );
- Integer churchDonorKarma = ChurchDB.getDonorKarma( donor );
- if ( churchDonorKarma != null && churchDonorKarma - donationAmount <= 0 )
- {
- ChurchDB.modDonorTitle( donor, " " );
- }
- event.respondWith( "Thank you for your donation of " + donationAmount + " " + donor + "!" );
- }
- }
-
- private void viewChurchTotal( GenericMessageEvent event )
- {
- long totalDonations = ChurchDB.getTotalDonations();
- long churchTotal = totalDonations + ChurchDB.getTotalNonDonations();
- event.respondWith( "The church coffers contains " + churchTotal + ", of which " + totalDonations
- + " was contributed by its loyal believers." );
- }
-
- // @Handler
- private void viewTopDonors( GenericMessageEvent event )
- {
- // Matcher matcher = topDonorsPattern.matcher( event.getMessage() );
- // if ( matcher.matches() )
- // {
- Set nouns;
- nouns = ChurchDB.getDonorsByRanks( 0, 3 );
- if ( nouns != null && nouns.size() > 0 )
- {
- event.respondWith( "NICK AMOUNT TITLE" );
- for ( String noun : nouns )
- {
- replyWithRankAndDonationAmount( noun, event );
- }
- }
- else
- {
- event.respondWith( "The church does not have enough followers yet." );
- }
- // }
- }
-
- // @Handler
- private void setTitle( GenericMessageEvent event, Matcher matcher )
- {
- // Matcher matcher = setTitlesPattern.matcher( event.getMessage() );
- // if ( matcher.matches() )
- // {
- String donor = BaseNick.toBaseNick( event.getUser().getNick() );
- Integer donorRank = ChurchDB.getDonorRank( donor );
- if ( donorRank != null && donorRank > 4 )
- {
- event.respondWith( "You must be a top donor to set your title." );
- return;
- }
-
- String newTitle = matcher.group( 1 );
- if ( newTitle.length() > 40 )
- {
- newTitle = newTitle.substring( 0, 39 );
- }
-
- String oldTitle = ChurchDB.getDonorTitle( donor );
- ChurchDB.modDonorTitle( donor, newTitle );
- event.respondWith( donor + " is now to be referred to as " + newTitle + " instead of " + oldTitle + "." );
- // }
- }
-
- private void inquisit( GenericMessageEvent event, Matcher matcher )
- {
- if ( currentVote != null )
- {
- event.respondWith( "There is an ongoing inquisition against " + currentVote.getDecisionTarget()
- + ". Please use !aye or !nay to decide their fate" );
- return;
- }
-
- String donor = BaseNick.toBaseNick( event.getUser().getNick() );
- Integer donorRank = ChurchDB.getDonorRank( donor );
- if ( donorRank == null || donorRank > 4 )
- {
- event.respondWith( "You must be a top donor to start an inquisition." );
- return;
- }
-
- StringBuilder membersToPing = new StringBuilder();
- Set nouns = ChurchDB.getDonorsByRanks( 0, 3 );
- if ( nouns != null && nouns.size() > 3 )
- {
- for ( String noun : nouns )
- {
- membersToPing.append( noun ).append( " " );
- }
- }
- else
- {
- event.respondWith( "There are not enough members of the church to invoke a vote" );
- return;
- }
-
- String inquisitedNick = BaseNick.toBaseNick( matcher.group( 1 ) );
- Integer nounKarma = ChurchDB.getDonorKarma( inquisitedNick );
- if ( nounKarma == null || nounKarma == 0 )
- {
- event.respondWith( "You cannot start an inquisition against someone who has no donation value." );
- return;
- }
-
- event.respondWith( "An inquisition has started against " + inquisitedNick
- + ". Please cast your votes with !aye or !nay" );
- event.respondWith( membersToPing.toString() );
- startInquisitionVote( event, inquisitedNick, donorRank );
- }
-
- private synchronized void startInquisitionVote( GenericMessageEvent event, String inquisitedNick, int donorRank )
- {
- currentVote = new VoteEvent();
- currentVote.setDecisionTarget( inquisitedNick );
- currentVote.updateVoteAye( donorRank );
- startTimer( event );
- }
-
- private void inquisitDecision( GenericMessageEvent event, Matcher matcherAye, Matcher matcherNay )
- {
- if ( currentVote == null )
- {
- event.respondWith( "There is no ongoing vote." );
- return;
- }
-
- String donor = BaseNick.toBaseNick( event.getUser().getNick() );
- Integer donorRank = ChurchDB.getDonorRank( donor );
- if ( donorRank == null || donorRank > 4 )
- {
- event.respondWith( "You must be a top donor to vote." );
- return;
- }
-
- if ( matcherAye.matches() )
- {
- currentVote.updateVoteAye( donorRank );
- }
- else if ( matcherNay.matches() )
- {
- currentVote.updateVoteNay( donorRank );
- }
-
- StringBuilder currentChoices = new StringBuilder();
- int i = 1;
- Set nouns = ChurchDB.getDonorsByRanks( 0, 3 );
- for ( String noun : Objects.requireNonNull( nouns ) )
- {
- currentChoices.append( noun ).append( " (" ).append( currentVote.getDonorVote( i++ ) ).append( ") " );
- }
- event.respondWith( currentChoices.toString() );
- }
-
- private void startTimer( final GenericMessageEvent event )
- {
- timer.submit( () -> {
- try
- {
- Thread.sleep( TIMEOUT );
- timeoutTimer( event );
- }
- catch ( InterruptedException e )
- {
- e.printStackTrace();
- }
- } );
- }
-
- private void timeoutTimer( GenericMessageEvent event )
- {
- String message = "Voting is now finished\n";
- if ( currentVote.isDecisionYes() )
- {
- message += "The vote to inquisit " + currentVote.getDecisionTarget() + " has passed. "
- + currentVote.getDecisionTarget() + " will be stripped of their karma.";
- Integer donorKarma = ChurchDB.getDonorKarma( currentVote.getDecisionTarget() );
- if ( donorKarma != null )
- {
- ChurchDB.modNonDonorKarma( donorKarma );
- ChurchDB.removeDonor( currentVote.getDecisionTarget() );
- }
- }
- else
- {
- message += "The vote to inquisit " + currentVote.getDecisionTarget() + " has failed. Nothing will happen.";
- }
- for ( String line : message.split( "\n" ) )
- {
- event.respondWith( line );
- }
- currentVote = null;
- }
-
- private static void replyWithRankAndDonationAmount( String noun, GenericMessageEvent event )
- {
- Integer nounKarma = ChurchDB.getDonorKarma( noun );
- String nounTitle = ChurchDB.getDonorTitle( noun );
- if ( nounKarma != null )
- {
- StringBuilder sb = new StringBuilder();
- sb.append( noun );
- sb.reverse();
- String nounString = sb.toString();
- int nounLength = sb.length();
- int spaceLength;
- if ( nounLength <= 14 )
- {
- spaceLength = 15 - nounLength;
- }
- else
- {
- nounString = nounString.substring( 0, 14 );
- spaceLength = 1;
- }
-
- String spaces_first = String.format( "%" + spaceLength + "s", "" );
- if ( nounKarma.toString().length() <= 9 )
- {
- spaceLength = 10 - nounKarma.toString().length();
- }
-
- String spaces_second = String.format( "%" + spaceLength + "s", "" );
- event.respondWith( nounString + spaces_first + nounKarma.toString() + spaces_second + nounTitle );
- }
- }
-
- private static class VoteEvent
- {
- private boolean[] topDonorDecisions;
-
- private String decisionTarget;
-
- VoteEvent()
- {
- topDonorDecisions = new boolean[4];
- for ( int i = 0; i < 4; i++ )
- {
- topDonorDecisions[i] = false;
- }
- decisionTarget = null;
- }
-
- void updateVoteAye( int donorRank )
- {
- topDonorDecisions[donorRank - 1] = true;
- }
-
- void updateVoteNay( int donorRank )
- {
- topDonorDecisions[donorRank - 1] = false;
- }
-
- boolean getDonorVote( int donorRank )
- {
- return topDonorDecisions[donorRank - 1];
- }
-
- boolean isDecisionYes()
- {
- int totalValue = 0;
- if ( topDonorDecisions[0] )
- totalValue += 5;
- if ( topDonorDecisions[1] )
- totalValue += 3;
- if ( topDonorDecisions[2] )
- totalValue += 3;
- if ( topDonorDecisions[3] )
- totalValue += 1;
- return totalValue > 6;
- }
-
- void setDecisionTarget( String target )
- {
- decisionTarget = target;
- }
-
- String getDecisionTarget()
- {
- return decisionTarget;
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Decide.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Decide.java
deleted file mode 100644
index 8e0a4ecc..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Decide.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Decide
- extends ListenerAdapter
-{
- private static final Pattern choicePattern = Pattern.compile( "(?:(.+?)(?:\\s*\\|\\s*|\\s*$))" );
-
- private static final Pattern decidePattern =
- Pattern.compile( "!decide (\\[.*\\] )?(\\{.*\\} )?(" + choicePattern + "+)" );
-
- private static final Random random = new Random();
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = decidePattern.matcher( text );
-
- if ( matcher.matches() )
- {
- String noun = matcher.group( 1 );
- if ( noun == null || noun.isEmpty() )
- {
- noun = "you";
- }
- if ( !noun.equals( "you" ) )
- {
- noun = noun.substring( 1, noun.length() - 2 );
- }
-
- String prefix = matcher.group( 2 );
- if ( prefix == null )
- {
- prefix = "";
- }
-
- if ( prefix.length() >= 2 )
- {
- prefix = prefix.substring( 1, prefix.length() - 2 ).concat( " " );
- }
- String choicesBlob = matcher.group( 3 );
-
- // Process choices blob
- List choices = new LinkedList();
- Matcher choiceMatcher = choicePattern.matcher( choicesBlob );
- while ( choiceMatcher.find() )
- {
- choices.add( choiceMatcher.group( 1 ) );
- }
-
- StringBuilder sb = new StringBuilder();
-
- if ( choices.size() == 1 )
- {
- sb.append( "If you already know what you want to do, what are you bugging me for? You should " );
- }
- else
- {
- sb.append( "I think " + noun + " should " );
- }
-
- sb.append( prefix );
-
- int selection = random.nextInt( choices.size() );
- sb.append( choices.get( selection ).trim() );
-
- sb.append( ". Go do that now." );
-
- event.respondWith( sb.toString() );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Excuses.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Excuses.java
deleted file mode 100644
index 830d70e4..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Excuses.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.db.ExcuseDB;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Excuses
- extends ListenerAdapter
-{
- private static final Pattern excusePattern = Pattern.compile( "!excuse" );
-
- @Override
- public void onGenericMessage( GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = excusePattern.matcher( text );
-
- if ( matcher.matches() )
- {
- String excuse = ExcuseDB.getRandExcuse();
- if ( excuse != null )
- {
- event.respondWith( excuse );
- }
- else
- {
- event.respondWith( "No excuses available" );
- }
- }
- }
-
- // TODO method to add excuses
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/FakeNews.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/FakeNews.java
deleted file mode 100644
index 4adedfbc..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/FakeNews.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Copyright (C) 2019 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.util.LimitCommand;
-import com.oldterns.vilebot.util.NewsParser;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.LinkedHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class FakeNews
- extends NewsParser
-{
-
- private static final Logger logger = LoggerFactory.getLogger( FakeNews.class );
-
- private static final String DEFAULT_CATEGORY = "canada";
-
- private static final LinkedHashMap> fakeNewsFeedsByCategory =
- new LinkedHashMap<>();
- static
- {
- try
- {
- fakeNewsFeedsByCategory.put( "canada",
- new ImmutablePair<>( "Countries",
- new URL( "https://www.thebeaverton.com/rss" ) ) );
- fakeNewsFeedsByCategory.put( "usa", new ImmutablePair<>( "Countries",
- new URL( "https://www.theonion.com/rss" ) ) );
- fakeNewsFeedsByCategory.put( "belgium",
- new ImmutablePair<>( "Countries", new URL( "https://nordpresse.be/rss" ) ) );
- fakeNewsFeedsByCategory.put( "france",
- new ImmutablePair<>( "Countries", new URL( "http://www.legorafi.fr/rss" ) ) );
- fakeNewsFeedsByCategory.put( "india", new ImmutablePair<>( "Countries",
- new URL( "http://www.fakingnews.com/rss" ) ) );
- fakeNewsFeedsByCategory.put( "russia",
- new ImmutablePair<>( "Countries", new URL( "https://fognews.ru/rss" ) ) );
- fakeNewsFeedsByCategory.put( "serbia",
- new ImmutablePair<>( "Countries", new URL( "https://www.njuz.net/rss" ) ) );
- fakeNewsFeedsByCategory.put( "venezuela",
- new ImmutablePair<>( "Countries",
- new URL( "http://feeds.feedburner.com/elchiguirebipolar" ) ) );
- fakeNewsFeedsByCategory.put( "newzealand",
- new ImmutablePair<>( "Countries",
- new URL( "http://www.thecivilian.co.nz/rss" ) ) );
- }
- catch ( MalformedURLException e )
- {
- logger.error( "Error loading fake news URLs" );
- throw new RuntimeException( e );
- }
- }
-
- private static final Pattern FAKE_NEWS_PATTERN = Pattern.compile( "^!fakenews(?: ([a-zA-Z]+)|)" );
-
- private static final Pattern FAKE_NEWS_HELP_PATTERN = Pattern.compile( "^!fakenews help" );
-
- private final String HELP_MESSAGE = generateHelpMessage();
-
- private final String HELP_COMMAND = "'!fakenews help'";
-
- public static LimitCommand limitCommand = new LimitCommand();
-
- private static final String RESTRICTED_CHANNEL = Vilebot.getConfig().get( "ircChannel1" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = FAKE_NEWS_PATTERN.matcher( text );
- Matcher helpMatcher = FAKE_NEWS_HELP_PATTERN.matcher( text );
-
- if ( helpMatcher.matches() )
- {
- for ( String line : HELP_MESSAGE.split( "\n" ) )
- {
- event.respondPrivateMessage( line );
- }
- }
- else if ( matcher.matches() )
- {
- currentNews( event, matcher, fakeNewsFeedsByCategory, DEFAULT_CATEGORY, HELP_COMMAND, limitCommand,
- RESTRICTED_CHANNEL, logger );
- }
- }
-
- @Override
- protected String generateHelpMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "Fake News Categories (example: !fakenews canada):" );
- sb.append( "\n" );
-
- for ( String category : fakeNewsFeedsByCategory.keySet() )
- {
- sb.append( " { " + category + " }" );
- }
-
- return sb.toString();
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Fortune.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Fortune.java
deleted file mode 100644
index 4e314abd..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Fortune.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Created by ipun on 15/05/16.
- */
-public class Fortune
- extends ListenerAdapter
-{
- private static final Pattern FORTUNE_PATTERN = Pattern.compile( "^!fortune(.*)" );
-
- private static final String FORTUNE_LIST_PATH = Vilebot.getConfig().get( "FortuneList" );
-
- private ArrayList fortune = loadFortunes();
-
- private static final String DIRTY_ARG = "dirty";
-
- @Override
- public void onGenericMessage( GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher fortuneMatcher = FORTUNE_PATTERN.matcher( text );
- try
- {
- if ( fortuneMatcher.matches() )
- {
- String dirty = fortuneMatcher.group( 1 );
- if ( dirty == null || dirty.isEmpty() )
- {
- fortuneReply( event );
- }
- else if ( dirty.equals( DIRTY_ARG ) )
- {
- event.respondWith( "oooo you dirty" );
- }
- }
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- System.exit( 1 );
- }
- }
-
- private void fortuneReply( GenericMessageEvent event )
- {
- String randomFortune = fortune.get( new Random().nextInt( fortune.size() ) );
- event.respondWith( randomFortune );
- }
-
- private ArrayList loadFortunes()
- {
- try
- {
- List lines = Files.readAllLines( Paths.get( FORTUNE_LIST_PATH ), Charset.forName( "UTF-8" ) );
- return new ArrayList<>( lines );
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- System.exit( 1 );
- }
- return null;
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Help.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Help.java
deleted file mode 100644
index 70c29d56..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Help.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Help
- extends ListenerAdapter
-{
- private static final Pattern helpPattern = Pattern.compile( "!help" );
-
- private static final String helpMessage = generateHelpMessage();
-
- /**
- * Reply to user !help command with help info
- */
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = helpPattern.matcher( text );
-
- if ( matcher.matches() )
- {
- event.respondPrivateMessage( helpMessage );
- }
- }
-
- private static String generateHelpMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "Available Commands:" );
-
- sb.append( "\n" );
- sb.append( " General:" );
- sb.append( " { !help }" );
- sb.append( " { !ping }" );
- sb.append( "\n" );
- sb.append( " Karma:" );
- sb.append( " { noun++ }" );
- sb.append( " { noun-- }" );
- sb.append( " { noun+- }" );
- sb.append( " { !rank }" );
- sb.append( " { !revrank }" );
- sb.append( " { !rankn }" );
- sb.append( " { !revrankn }" );
- sb.append( " { !topthree }" );
- sb.append( " { !bottomthree }" );
- sb.append( " { !roll [for] [] }" );
- sb.append( " { !rollcancel }" );
- sb.append( " { !transfer }" );
- sb.append( " { !transfercancel }" );
- sb.append( "\n" );
- sb.append( " Games:" );
- sb.append( " { !rps []}" );
- sb.append( " { !rpsrules }" );
- sb.append( " { !rpscancel }" );
- sb.append( " { !(rock|paper|scissors) }" );
- sb.append( " { !omgword }" );
- sb.append( " { !answer }" );
- sb.append( " { !jeopardy }" );
- sb.append( " { !whatis }" );
- sb.append( " { !whois }" );
- sb.append( " { !countdown } " );
- sb.append( " { !solution < numbered answer using symbols +-/*() > } " );
- sb.append( " { !countdownrules } " );
- sb.append( "\n" );
- sb.append( " Fun:" );
- sb.append( " { !kaomoji }" );
- sb.append( " { !excuse }" );
- sb.append( " { !randommeme }" );
- sb.append( " { !chuck }" );
- sb.append( " { !dance }" );
- sb.append( " { !speak }" );
- sb.append( "\n" );
- sb.append( " Facts/Quotes:" );
- sb.append( " { !fact [!jaziz] }" );
- sb.append( " { !quote [!jaziz] }" );
- sb.append( " { !factrandom5 }" );
- sb.append( " { !quoterandom5 }" );
- sb.append( " { !factdump }" );
- sb.append( " { !quotedump }" );
- sb.append( " { !factadd }" );
- sb.append( " { !quoteadd }" );
- sb.append( " { !factsearch [!jaziz] }" );
- sb.append( " { !quotesearch [!jaziz] }" );
- sb.append( " { !factnumber }" );
- sb.append( " { !quotenumber }" );
- sb.append( "\n" );
- sb.append( " Utility:" );
- sb.append( " { s/foo/bar[/[g] []] }" );
- sb.append( " { !decide [] {} [|]... }" );
- sb.append( " { !weather [] }" );
- sb.append( " { !lessweather [] }" );
- sb.append( " { !moreweather [] }" );
- sb.append( " { !tellme }" );
- sb.append( " { !infoon }" );
- sb.append( " { !ttc }" );
- sb.append( " { !remindme }" );
- sb.append( " { !ascii [] }" );
- sb.append( " { !asciifonts }" );
- sb.append( " { !news [] }" );
- sb.append( " { !fakenews [] }" );
- sb.append( " { !downorjustme }" );
- sb.append( "\n" );
- sb.append( " Userlists:" );
- sb.append( " { !list }" );
- sb.append( " { !listadd [,|, | ][]... }" );
- sb.append( " { !listrem [,|, | ][]... }" );
- sb.append( " { listname: [message] }" );
- sb.append( "\n" );
- sb.append( " Church:" );
- sb.append( " { !churchtotal }" );
- sb.append( " { !donate }" );
- sb.append( " { !gospel }" );
- sb.append( " { !inquisit }" );
- sb.append( " { !settitle }" );
- sb.append( " { !topdonors }" );
- return sb.toString();
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/ImageToAscii.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/ImageToAscii.java
deleted file mode 100644
index 7287a332..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/ImageToAscii.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.util.ASCII;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import javax.imageio.ImageIO;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.net.URL;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Created by eunderhi on 17/08/15.
- */
-
-public class ImageToAscii
- extends ListenerAdapter
-{
- private static final Pattern questionPattern = Pattern.compile( "^!(convert)\\s(.+)$" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = questionPattern.matcher( text );
-
- if ( matcher.matches() )
- {
- String URL = matcher.group( 2 );
- try
- {
- String image = convertImage( URL );
- event.respondWith( image );
- }
- catch ( Exception e )
- {
- event.respondWith( "Could not convert image." );
- }
- }
- }
-
- private String convertImage( String strURL )
- throws Exception
- {
- URL url = new URL( strURL );
- BufferedImage image = ImageIO.read( url );
- image = shrink( image );
- return new ASCII().convert( image );
- }
-
- private BufferedImage shrink( BufferedImage image )
- {
- int MAX_WIDTH = 50;
- int height = image.getHeight();
- int width = image.getWidth();
- float ratio = (float) height / width;
- int newHeight = Math.round( ( MAX_WIDTH * ratio ) * 0.56f );
-
- BufferedImage newImage = new BufferedImage( MAX_WIDTH, newHeight, BufferedImage.TYPE_INT_RGB );
-
- Graphics g = newImage.createGraphics();
- g.drawImage( image, 0, 0, MAX_WIDTH, newHeight, null );
- g.dispose();
- return newImage;
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Inspiration.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Inspiration.java
deleted file mode 100644
index bb0c12d9..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Inspiration.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Created by ipun on 15/05/16.
- */
-
-public class Inspiration
- extends ListenerAdapter
-{
- private static final Pattern FORTUNE_PATTERN = Pattern.compile( "^!inspiration(.*)" );
-
- private static final String FORTUNE_LIST_PATH = Vilebot.getConfig().get( "InspirationList" );
-
- private static final String FORTUNE_INDEX_PATH = Vilebot.getConfig().get( "InspirationIndex" );
-
- private ArrayList inspiration = loadInspirations();
-
- private List inspirationIndex = loadInspirationIndex();
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher inspirationMatcher = FORTUNE_PATTERN.matcher( text );
- try
- {
- if ( inspirationMatcher.matches() )
- {
- String dirty = inspirationMatcher.group( 1 );
- if ( dirty == null || dirty.isEmpty() )
- {
- inspirationReply( event );
- }
-
- }
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- System.exit( 1 );
- }
- }
-
- private void inspirationReply( GenericMessageEvent event )
- {
- int index = Integer.parseInt( inspirationIndex.get( new Random().nextInt( inspirationIndex.size() - 1 ) ) );
- String line = inspiration.get( index );
- while ( !line.matches( "%" ) )
- {
- event.respondWith( line );
- line = inspiration.get( ++index );
- }
- }
-
- private ArrayList loadInspirations()
- {
- try
- {
- List lines = Files.readAllLines( Paths.get( FORTUNE_LIST_PATH ), Charset.forName( "UTF-8" ) );
- return new ArrayList<>( lines );
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- System.exit( 1 );
- }
- return null;
- }
-
- private List loadInspirationIndex()
- {
- try
- {
- String lines = new String( Files.readAllBytes( Paths.get( FORTUNE_INDEX_PATH ) ) );
- return Arrays.asList( lines.split( "\n" ) );
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- System.exit( 1 );
- }
- return null;
- }
-
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Kaomoji.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Kaomoji.java
deleted file mode 100644
index e3995503..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Kaomoji.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import twitter4j.JSONObject;
-
-import java.io.FileNotFoundException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Scanner;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Created by ltulloch on 07/24/18.
- */
-public class Kaomoji
- extends ListenerAdapter
-{
-
- private static final String API_URL = "https://jckcthbrt.stdlib.com/kaomoji/?search=";
-
- private static final String API_FORMAT = "/json";
-
- private static final Pattern kaomojiPattern = Pattern.compile( "^!kaomoji (.+)" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- Matcher questionMatcher = kaomojiPattern.matcher( event.getMessage() );
- if ( questionMatcher.matches() )
- {
- String message = questionMatcher.group( 1 );
- try
- {
- message = kaomojiify( message );
- event.respondWith( message );
- }
- catch ( Exception e )
- {
- event.respondWith( "ⓃⒶⓃⒾ(☉൧ ಠ ꐦ)" );
- e.printStackTrace();
- }
- }
- }
-
- private String kaomojiify( String message )
- throws Exception
- {
- String[] words = splitWords( message );
- String kaomoji = "";
- if ( words.length <= 0 )
- {
- return "ⓃⒶⓃⒾ(☉൧ ಠ ꐦ)";
- }
- switch ( words[0] )
- {
- case "wat":
- words[0] = "confused";
- break;
- case "nsfw":
- case "wtf":
- kaomoji = "ⓃⒶⓃⒾ(☉൧ ಠ ꐦ)";
- break;
- case "vilebot":
- kaomoji = "( ͡° ͜ʖ ͡° )";
- break;
- default:
- kaomoji = getKaomoji( words[0] );
- break;
- }
-
- if ( kaomoji.isEmpty() )
- {
- kaomoji = "щ(ಥдಥщ)";
- }
- return kaomoji;
- }
-
- private String[] splitWords( String message )
- {
- return message.split( "\\b" );
- }
-
- private String getKaomoji( String word )
- throws Exception
- {
- String emoji = "";
- JSONObject json = new JSONObject( getContent( word ) );
- if ( json.has( "success" ) )
- {
- emoji = json.getString( "emoji" );
- }
- return emoji;
- }
-
- private String getContent( String word )
- throws Exception
- {
- String content;
- URLConnection connection;
- connection = new URL( API_URL + word ).openConnection();
- connection.addRequestProperty( "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" );
- try
- {
- Scanner scanner = new Scanner( connection.getInputStream() );
- scanner.useDelimiter( "\\Z" );
- content = scanner.next();
- return content;
- }
- catch ( FileNotFoundException e )
- {
- return "{}";
- }
- }
-
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Karma.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Karma.java
deleted file mode 100644
index e0096f29..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Karma.java
+++ /dev/null
@@ -1,425 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.db.ChurchDB;
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.db.KarmaDB;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.Ignore;
-import com.oldterns.vilebot.util.Sessions;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.JoinEvent;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.events.PrivateMessageEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import org.pircbotx.output.OutputIRC;
-
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Karma
- extends ListenerAdapter
-{
- private static final Pattern nounPattern = Pattern.compile( "\\S+" );
-
- private static final Pattern nickBlobPattern = Pattern.compile( "(?:(" + nounPattern + "?)(?:, +| +|$))" );
-
- private static final Pattern incBlobPattern = Pattern.compile( "(?:(" + nounPattern + "?\\+\\+)(?:, +| +|$))" );
-
- private static final Pattern decBlobPattern = Pattern.compile( "(?:(" + nounPattern + "?--)(?:, +| +|$))" );
-
- private static final Pattern incOrDecBlobPattern = Pattern.compile( "(?:(" + nounPattern + "?\\+-)(?:, +| +|$))" );
-
- private static final Pattern incrementPattern = Pattern.compile( "(?:^|^.*\\s+)(" + incBlobPattern + "+)(?:.*|$)" );
-
- private static final Pattern decrementPattern = Pattern.compile( "(?:^|^.*\\s+)(" + decBlobPattern + "+)(?:.*|$)" );
-
- private static final Pattern incOrDecPattern =
- Pattern.compile( "(?:^|^.*\\s+)(" + incOrDecBlobPattern + "+)(?:.*|$)" );
- // The opening (?:^|^.*\\s+) and closing (?:.*|$) are needed when only part of the message is ++ or -- events
-
- private static final Pattern selfKarmaQueryPattern = Pattern.compile( "^\\s*!(rev|)rank\\s*$" );
-
- private static final Pattern karmaQueryPattern = Pattern.compile( "!(rev|)rank (" + nickBlobPattern + "+)" );
-
- private static final Pattern ranknPattern = Pattern.compile( "!(rev|)rankn ([0-9]+)\\s*" );
-
- private static final Pattern topBottomThreePattern = Pattern.compile( "!(top|bottom)three\\s*" );
-
- private static final Pattern removePattern = Pattern.compile( "!admin unrank (" + nounPattern + ")\\s*" );
-
- private static final Pattern totalPattern = Pattern.compile( "^!total" );
-
- @Override
- public void onJoin( JoinEvent event ) // announce karma on join
- {
- String noun = BaseNick.toBaseNick( Objects.requireNonNull( event.getUser() ).getNick() );
- OutputIRC outputQ = event.getBot().send();
- String replyTarget = event.getChannel().getName();
- if ( !Ignore.getOnJoin().contains( noun ) )
- replyWithRankAndKarma( noun, outputQ, replyTarget, false, false, true );
- }
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- OutputIRC outputQ = event.getBot().send();
- String replyTarget;
- if ( event instanceof PrivateMessageEvent )
- replyTarget = event.getUser().getNick();
- else if ( event instanceof MessageEvent )
- replyTarget = ( (MessageEvent) event ).getChannel().getName();
- else
- return;
-
- Matcher incMatcher = incrementPattern.matcher( text );
- Matcher decMatcher = decrementPattern.matcher( text );
- Matcher incOrDecMatcher = incOrDecPattern.matcher( text );
- Matcher specificMatcher = karmaQueryPattern.matcher( text );
- Matcher selfMatcher = selfKarmaQueryPattern.matcher( text );
- Matcher rankNumberMatcher = ranknPattern.matcher( text );
- Matcher totalKarmaMatcher = totalPattern.matcher( text );
- Matcher topBottomThreeMatcher = topBottomThreePattern.matcher( text );
- Matcher unrankMatcher = removePattern.matcher( text );
-
- if ( incMatcher.matches() )
- karmaInc( event, incMatcher );
- if ( decMatcher.matches() )
- karmaDec( event, decMatcher );
- if ( incOrDecMatcher.matches() )
- karmaIncOrDec( event, incOrDecMatcher );
- if ( specificMatcher.matches() )
- specificKarmaQuery( event, outputQ, replyTarget, specificMatcher );
- if ( selfMatcher.matches() )
- selfKarmaQuery( event, outputQ, replyTarget, selfMatcher );
- if ( rankNumberMatcher.matches() )
- rankNumber( event, outputQ, replyTarget, rankNumberMatcher );
- if ( totalKarmaMatcher.matches() )
- totalKarma( event );
- if ( topBottomThreeMatcher.matches() )
- topBottomThree( event, outputQ, replyTarget, topBottomThreeMatcher );
- if ( unrankMatcher.matches() )
- unrank( event, outputQ, replyTarget, unrankMatcher );
- }
-
- private void karmaInc( GenericMessageEvent event, Matcher incMatcher )
- {
- if ( isPrivate( event ) )
- {
- KarmaDB.modNounKarma( Objects.requireNonNull( event.getUser() ).getNick(), -1 );
- return;
- }
- // Prevent users from increasing karma outside of #TheFoobar
- if ( !( (MessageEvent) event ).getChannel().getName().equals( Vilebot.getConfig().get( "ircChannel1" ) ) )
- {
- event.respondWith( "You must be in " + Vilebot.getConfig().get( "ircChannel1" )
- + " to give or receive karma." );
- return;
- }
-
- // If one match is found, take the entire text of the message (group(0)) and check each word
- // This is needed in the case that only part of the message is karma events (ie "wow anestico++")
- String wordBlob = incMatcher.group( 0 );
- String sender = BaseNick.toBaseNick( Objects.requireNonNull( event.getUser() ).getNick() );
-
- Set nicks = new HashSet<>();
- Matcher nickMatcher = incBlobPattern.matcher( wordBlob );
- while ( nickMatcher.find() )
- {
- nicks.add( BaseNick.toBaseNick( nickMatcher.group( 1 ) ) );
- }
-
- boolean insult = false;
-
- for ( String nick : nicks )
- {
- if ( !nick.equals( sender ) )
- KarmaDB.modNounKarma( nick, 1 );
- else
- insult = true;
- }
-
- if ( insult )
- // TODO insult generator?
- event.respondWith( "I think I'm supposed to insult you now." );
- }
-
- private void karmaDec( GenericMessageEvent event, Matcher decMatcher )
- {
- if ( isPrivate( event ) )
- {
- KarmaDB.modNounKarma( Objects.requireNonNull( event.getUser() ).getNick(), -1 );
- return;
- }
- // Prevent users from decreasing karma outside of #TheFoobar
- if ( !( (MessageEvent) event ).getChannel().getName().equals( Vilebot.getConfig().get( "ircChannel1" ) ) )
- {
- event.respondWith( "You must be in " + Vilebot.getConfig().get( "ircChannel1" )
- + " to give or receive karma." );
- return;
- }
- // If one match is found, take the entire text of the message (group(0)) and check each word
- String wordBlob = decMatcher.group( 0 );
-
- List nicks = new LinkedList<>();
- Matcher nickMatcher = decBlobPattern.matcher( wordBlob );
- while ( nickMatcher.find() )
- {
- nicks.add( BaseNick.toBaseNick( nickMatcher.group( 1 ) ) );
- }
-
- boolean insult = false;
- String botNick = event.getBot().getNick();
- for ( String nick : nicks )
- {
- if ( !nick.equals( botNick ) )
- KarmaDB.modNounKarma( nick, -1 );
- else
- insult = true;
- }
-
- if ( insult )
- // TODO insult generator?
- event.respondWith( "I think I'm supposed to insult you now." );
- }
-
- private void karmaIncOrDec( GenericMessageEvent event, Matcher incOrDecMatcher )
- {
- if ( isPrivate( event ) )
- {
- KarmaDB.modNounKarma( Objects.requireNonNull( event.getUser() ).getNick(), -1 );
- return;
- }
-
- // Prevent users from increasing karma outside of #TheFoobar
- if ( !( (MessageEvent) event ).getChannel().getName().equals( Vilebot.getConfig().get( "ircChannel1" ) ) )
- {
- event.respondWith( "You must be in " + Vilebot.getConfig().get( "ircChannel1" )
- + " to give or receive karma." );
- return;
- }
-
- // If one match is found, take the entire text of the message (group(0)) and check each word
- // This is needed in the case that only part of the message is karma events (ie "wow anestico++")
- String wordBlob = incOrDecMatcher.group( 0 );
- String sender = BaseNick.toBaseNick( Objects.requireNonNull( event.getUser() ).getNick() );
-
- Set nicks = new HashSet<>();
- Matcher nickMatcher = incOrDecBlobPattern.matcher( wordBlob );
- while ( nickMatcher.find() )
- {
- nicks.add( BaseNick.toBaseNick( nickMatcher.group( 1 ) ) );
- }
-
- boolean insult = false;
-
- for ( String nick : nicks )
- {
- if ( !nick.equals( sender ) )
- decideIncOrDec( event, nick );
- else
- insult = true;
- }
-
- if ( insult )
- // TODO insult generator?
- event.respondWith( "I think I'm supposed to insult you now." );
- }
-
- private void decideIncOrDec( GenericMessageEvent event, String nick )
- {
- int karma = 0;
- Random rand = new Random();
- while ( karma == 0 )
- {
- karma = rand.nextInt( 3 ) - 1;
- }
- String reply = nick + " had their karma ";
- reply += karma == 1 ? "increased" : "decreased";
- reply += " by 1";
- event.respondWith( reply );
- KarmaDB.modNounKarma( nick, karma );
- }
-
- private boolean isPrivate( GenericMessageEvent event )
- {
- return event instanceof PrivateMessageEvent;
- }
-
- private void specificKarmaQuery( GenericMessageEvent event, OutputIRC outputQ, String replyTarget,
- Matcher specificMatcher )
- {
- String mode = specificMatcher.group( 1 );
- String nickBlob = specificMatcher.group( 2 );
-
- List nicks = new LinkedList<>();
- Matcher nickMatcher = nickBlobPattern.matcher( nickBlob );
- while ( nickMatcher.find() )
- {
- nicks.add( BaseNick.toBaseNick( nickMatcher.group( 1 ) ) );
- }
-
- boolean reverse = "rev".equals( mode );
-
- for ( String nick : nicks )
- {
- if ( !replyWithRankAndKarma( nick, outputQ, replyTarget, reverse ) )
- event.respondWith( nick + " has no karma." );
- }
- }
-
- private void selfKarmaQuery( GenericMessageEvent event, OutputIRC outputQ, String replyTarget, Matcher selfMatcher )
- {
- String mode = selfMatcher.group( 1 );
- String noun = BaseNick.toBaseNick( event.getUser().getNick() );
-
- boolean reverse = "rev".equals( mode );
- if ( !replyWithRankAndKarma( noun, outputQ, replyTarget, reverse ) )
- event.respondWith( noun + " has no karma." );
- }
-
- private void rankNumber( GenericMessageEvent event, OutputIRC outputQ, String replyTarget,
- Matcher rankNumberMatcher )
- {
- String mode = rankNumberMatcher.group( 1 );
- String place = rankNumberMatcher.group( 2 );
-
- boolean reverse = "rev".equals( mode );
-
- String noun;
- if ( reverse )
- noun = KarmaDB.getRevRankNoun( Long.parseLong( place ) );
- else
- noun = KarmaDB.getRankNoun( Long.parseLong( place ) );
-
- if ( noun != null )
- replyWithRankAndKarma( noun, outputQ, replyTarget, reverse );
- else
- event.respondWith( "No noun at that rank." );
- }
-
- private void totalKarma( GenericMessageEvent event )
- {
- event.respondWith( "" + KarmaDB.getTotalKarma() );
- }
-
- private void topBottomThree( GenericMessageEvent event, OutputIRC outputQ, String replyTarget,
- Matcher topBottomThreeMatcher )
- {
- String mode = topBottomThreeMatcher.group( 1 );
-
- Set nouns = null;
- if ( "top".equals( mode ) )
- {
- nouns = KarmaDB.getRankNouns( 0, 2 );
- }
- else if ( "bottom".equals( mode ) )
- {
- nouns = KarmaDB.getRevRankNouns( 0, 2 );
- }
-
- if ( nouns != null && nouns.size() > 0 )
- {
- for ( String noun : nouns )
- {
- replyWithRankAndKarma( noun, outputQ, replyTarget, false, true );
- }
- }
- else
- {
- event.respondWith( "No nouns at ranks 1 to 3." );
- }
- }
-
- private void unrank( GenericMessageEvent event, OutputIRC outputQ, String replyTarget, Matcher unrankMatcher )
- {
- // Admin-only command: remove all of a user's karma.
- // Matcher matcher = removePattern.matcher( event.getMessage() );
- String username = Sessions.getSession( event.getUser().getNick() );
-
- if ( GroupDB.isAdmin( username ) )
- {
- String noun = BaseNick.toBaseNick( unrankMatcher.group( 1 ) );
-
- if ( replyWithRankAndKarma( noun, outputQ, replyTarget ) )
- {
- event.respondWith( "Removing " + noun + "." );
- KarmaDB.remNoun( noun );
- }
- else
- {
- event.respondWith( noun + " isn't ranked." );
- }
- }
- }
-
- private static boolean replyWithRankAndKarma( String noun, OutputIRC outputQ, String replyTarget )
- {
- return replyWithRankAndKarma( noun, outputQ, replyTarget, false );
- }
-
- private static boolean replyWithRankAndKarma( String noun, OutputIRC outputQ, String replyTarget,
- boolean reverseOrder )
- {
- return replyWithRankAndKarma( noun, outputQ, replyTarget, reverseOrder, false );
- }
-
- private static boolean replyWithRankAndKarma( String noun, OutputIRC outputQ, String replyTarget,
- boolean reverseOrder, boolean obfuscateNick )
- {
- return replyWithRankAndKarma( noun, outputQ, replyTarget, reverseOrder, obfuscateNick, false );
- }
-
- private static boolean replyWithRankAndKarma( String noun, OutputIRC outputQ, String replyTarget,
- boolean reverseOrder, boolean obfuscateNick, boolean useTitle )
- {
- Integer nounRank;
- if ( reverseOrder )
- nounRank = KarmaDB.getNounRevRank( noun );
- else
- nounRank = KarmaDB.getNounRank( noun );
-
- Integer nounKarma = KarmaDB.getNounKarma( noun );
-
- if ( useTitle && ChurchDB.isTopDonor( noun ) )
- {
- String title = ChurchDB.getDonorTitle( noun );
- if ( title.trim().length() > 0 )
- {
- noun = title;
- }
- }
-
- if ( nounKarma != null )
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( noun );
- if ( obfuscateNick )
- sb.reverse();
-
- sb.append( " is " );
- sb.append( "ranked at " );
-
- if ( reverseOrder )
- sb.append( "(reverse) " );
-
- sb.append( "#" );
- sb.append( nounRank );
- sb.append( " with " );
- sb.append( nounKarma );
- sb.append( " points of karma." );
-
- outputQ.message( replyTarget, sb.toString() );
- return true;
- }
- return false;
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/KarmaRoll.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/KarmaRoll.java
deleted file mode 100644
index 7648e1c9..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/KarmaRoll.java
+++ /dev/null
@@ -1,318 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import java.security.SecureRandom;
-import java.util.Random;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.db.KarmaDB;
-import com.oldterns.vilebot.util.BaseNick;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-public class KarmaRoll
- extends ListenerAdapter
-{
- private static final Pattern rollPattern = Pattern.compile( "!roll(?: for|)(?: +([0-9]+)|)" );
-
- private static final Pattern cancelPattern = Pattern.compile( "!roll ?cancel" );
-
- private static final int UPPER_WAGER = 10;
-
- private RollGame currentGame;
-
- private final Object currentGameMutex = new Object();
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
-
- Matcher rollMatcher = rollPattern.matcher( text );
- Matcher cancelMatcher = cancelPattern.matcher( text );
-
- if ( rollMatcher.matches() )
- userHelp( event, rollMatcher );
- if ( cancelMatcher.matches() )
- manualCancel( event );
- }
-
- // @Handler
- private void userHelp( GenericMessageEvent event, Matcher rollMatcher )
- {
- String sender = BaseNick.toBaseNick( event.getUser().getNick() );
-
- // Infers ircChannel1 in JSON is #thefoobar for production Vilebot
- if ( !( event instanceof MessageEvent )
- || !( (MessageEvent) event ).getChannel().getName().equals( Vilebot.getConfig().get( "ircChannel1" ) ) )
- {
- event.respondWith( "You must be in " + Vilebot.getConfig().get( "ircChannel1" )
- + " to make or accept wagers." );
- return;
- }
-
- String rawWager = rollMatcher.group( 1 );
-
- synchronized ( currentGameMutex )
- {
- if ( currentGame == null )
- {
- // No existing game
-
- if ( rawWager == null )
- {
- // No game exists, but the user has not given a karma wager, so default to 10
- rawWager = "10";
- }
-
- // Check the wager value is in the acceptable range.
- int wager = Integer.parseInt( rawWager );
- Integer senderKarma = KarmaDB.getNounKarma( sender );
- senderKarma = senderKarma == null ? 0 : senderKarma;
-
- if ( !validWager( wager, senderKarma ) )
- {
- event.respondWith( wager
- + " isn't a valid wager. Must be greater than 0. If your wager is larger than " + UPPER_WAGER
- + " you must have at least as much karma as your wager." );
- }
- else
- {
- // Acceptable wager, start new game
-
- currentGame = new RollGame( sender, wager );
- event.respondWith( sender + " has rolled with " + wager + " karma points on the line. Who's up?" );
- }
- }
- else
- {
- // A game exists
-
- if ( rawWager != null )
- {
- // A game exists, but the user has given a karma wager, probably an error.
-
- String str = "A game is already active; started by " + currentGame.getFirstPlayerNick() + " for "
- + currentGame.getWager() + " karma. Use !roll to accept.";
- event.respondWith( str );
- }
- else
- {
- // A game exists, and no karma value was given. User is accepting the active wager/game.
-
- // The user that started a game cannot accept it
- if ( currentGame.getFirstPlayerNick().equals( sender ) )
- {
- event.respondWith( "You can't accept your own wager." );
- }
- else
- {
- GameResults result = currentGame.setSecondPlayer( sender );
-
- String firstPlayer = currentGame.getFirstPlayerNick();
- int firstRoll = result.getFirstPlayerRoll();
- String secondPlayer = currentGame.getSecondPlayerNick();
- int secondRoll = result.getSecondPlayerRoll();
-
- String winner = result.getWinnerNick();
- String loser = result.getLoserNick();
- int deltaKarma = currentGame.getWager();
-
- StringBuilder sb = new StringBuilder();
- sb.append( "Results: " );
- sb.append( firstPlayer );
- sb.append( " rolled " );
- sb.append( firstRoll );
- sb.append( ", and " );
- sb.append( secondPlayer );
- sb.append( " rolled " );
- sb.append( secondRoll );
- sb.append( ". " );
-
- if ( winner != null && loser != null )
- {
- sb.append( winner );
- sb.append( " takes " );
- sb.append( deltaKarma );
- sb.append( " from " );
- sb.append( loser );
- sb.append( "!!!" );
-
- KarmaDB.modNounKarma( winner, deltaKarma );
- KarmaDB.modNounKarma( loser, -1 * deltaKarma );
- }
- else
- {
- sb.append( "A tie!" );
- }
-
- event.respondWith( sb.toString() );
-
- // Reset
- currentGame = null;
-
- event.respondWith( "Play again?" );
- }
- }
- }
- }
- }
-
- /**
- * A valid wager is one that meets the following standards: 1. Is greater than 0. 2. If it is greater than 10, then
- * the user's karma is equal to or greater than the wager. The reasoning behind this is to have some base amount of
- * karma that user's can bet with, but also provide a way of betting large amounts of karma. To avoid destroying the
- * karma economy users cannot bet more than their current amount of karma.
- *
- * @param wager the amount wagered
- * @param senderKarma the wagerer's karma
- */
- private boolean validWager( int wager, int senderKarma )
- {
- return !( wager > 10 ) && ( wager > 0 ) || ( wager > 10 ) && ( senderKarma >= wager );
- }
-
- private void manualCancel( GenericMessageEvent event )
- {
- if ( !currentGame.getFirstPlayerNick().equals( BaseNick.toBaseNick( event.getUser().getNick() ) ) )
- {
- event.respondWith( "Only " + currentGame.getFirstPlayerNick() + " may cancel this game." );
- return;
- }
- synchronized ( currentGameMutex )
- {
- currentGame = null;
- }
- event.respondWith( "Roll game cancelled." );
- }
-
- private static class RollGame
- {
- private final int wager;
-
- private String firstPlayer;
-
- private String secondPlayer;
-
- RollGame( String firstPlayerNick, int wager )
- {
- if ( firstPlayerNick == null )
- throw new IllegalArgumentException( "firstPlayerNick can't be null" );
-
- this.firstPlayer = firstPlayerNick;
- this.wager = wager;
- }
-
- GameResults setSecondPlayer( String secondPlayerNick )
- {
- if ( secondPlayerNick == null )
- throw new IllegalArgumentException( "secondPlayerNick can't be null" );
-
- if ( secondPlayer == null )
- secondPlayer = secondPlayerNick;
- else
- throw new StateViolation( "Can't set the second player twice" );
-
- return new GameResults( firstPlayer, secondPlayer );
- }
-
- String getFirstPlayerNick()
- {
- return firstPlayer;
- }
-
- String getSecondPlayerNick()
- {
- return secondPlayer;
- }
-
- int getWager()
- {
- return wager;
- }
- }
-
- private static class GameResults
- {
- private static final int SIDES_OF_DIE = 6;
-
- private static final Random random = new SecureRandom();
-
- private final String firstPlayer;
-
- private final String secondPlayer;
-
- private Integer firstPlayerRoll = null;
-
- private Integer secondPlayerRoll = null;
-
- GameResults( String firstPlayer, String secondPlayer )
- {
- this.firstPlayer = firstPlayer;
- this.secondPlayer = secondPlayer;
-
- doRolls();
- }
-
- /**
- * @return The nick of the winning player, or null on a tie
- */
- String getWinnerNick()
- {
- if ( firstPlayerRoll > secondPlayerRoll )
- return firstPlayer;
- else if ( secondPlayerRoll > firstPlayerRoll )
- return secondPlayer;
- else
- return null; // Tied
- }
-
- /**
- * @return The nick of the losing player, or null on a tie
- */
- String getLoserNick()
- {
- if ( firstPlayerRoll < secondPlayerRoll )
- return firstPlayer;
- else if ( secondPlayerRoll < firstPlayerRoll )
- return secondPlayer;
- else
- return null; // Tied
- }
-
- /**
- * @return The value of the dice roll the first player got
- */
- int getFirstPlayerRoll()
- {
- return firstPlayerRoll;
- }
-
- /**
- * @return The value of the dice roll the second player got
- */
- int getSecondPlayerRoll()
- {
- return secondPlayerRoll;
- }
-
- private void doRolls()
- {
- firstPlayerRoll = random.nextInt( SIDES_OF_DIE ) + 1;
- secondPlayerRoll = random.nextInt( SIDES_OF_DIE ) + 1;
- }
- }
-
- private static class StateViolation
- extends RuntimeException
- {
- private static final long serialVersionUID = -7530159745349382310L;
-
- StateViolation( String message )
- {
- super( message );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/KarmaTransfer.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/KarmaTransfer.java
deleted file mode 100644
index 8b14ca51..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/KarmaTransfer.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/**
- * Copyright (C) 2020 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.db.KarmaDB;
-import com.oldterns.vilebot.util.BaseNick;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class KarmaTransfer
- extends ListenerAdapter
-{
- private static final Pattern transferPattern = Pattern.compile( "^!transfer\\s+(\\S+)\\s+([0-9]+)" );
-
- private static final Pattern cancelTransferPattern = Pattern.compile( "^!transfercancel" );
-
- private static final Pattern acceptTransferPattern = Pattern.compile( "^!accept" );
-
- private static final Pattern rejectTransferPattern = Pattern.compile( "^!reject" );
-
- private KarmaTransaction currentTransaction;
-
- private final Object currentTransferMutex = new Object();
-
- private static ExecutorService timer = Executors.newScheduledThreadPool( 1 );
-
- private static final long TIMEOUT = 30000L;
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
-
- Matcher transferMatcher = transferPattern.matcher( text );
- Matcher cancelTransferMatcher = cancelTransferPattern.matcher( text );
- Matcher acceptTransferMatcher = acceptTransferPattern.matcher( text );
- Matcher rejectTransferMatcher = rejectTransferPattern.matcher( text );
-
- if ( transferMatcher.matches() )
- transferKarma( event, transferMatcher );
- if ( cancelTransferMatcher.matches() )
- cancelTransfer( event );
- if ( acceptTransferMatcher.matches() )
- acceptTransfer( event );
- if ( rejectTransferMatcher.matches() )
- rejectTransfer( event );
- }
-
- private void transferKarma( GenericMessageEvent event, Matcher transferMatcher )
- {
- // Prevent users from transferring karma outside of #thefoobar
- if ( !( event instanceof MessageEvent )
- || !( (MessageEvent) event ).getChannel().getName().equals( Vilebot.getConfig().get( "ircChannel1" ) ) )
- {
- event.respondWith( "You must be in " + Vilebot.getConfig().get( "ircChannel1" ) + " to transfer karma." );
- return;
- }
-
- synchronized ( currentTransferMutex )
- {
- // No existing transfer
- if ( currentTransaction == null )
- {
- // Check if sender is the same as the receiver
- String sender = BaseNick.toBaseNick( event.getUser().getNick() );
- String receiver = BaseNick.toBaseNick( transferMatcher.group( 1 ) );
-
- if ( sender.equals( receiver ) )
- {
- event.respondWith( "Really? What's the point of transferring karma to yourself?" );
- return;
- }
-
- // Check if transfer amount is valid
- int transferAmount = Integer.parseInt( transferMatcher.group( 2 ) );
- Integer senderKarma = KarmaDB.getNounKarma( sender );
- senderKarma = senderKarma == null ? 0 : senderKarma;
-
- if ( !validAmount( transferAmount, senderKarma ) )
- {
- event.respondWith( transferAmount + " isn't a valid amount."
- + " Transfer amount must be greater than 0, and you must have at least as much karma"
- + " as the amount you want to transfer." );
- }
- else
- {
- // Valid amount, start transaction
- startTransaction( event, sender, receiver, transferAmount );
- }
- }
- // A transfer is active
- else
- {
- String sender = currentTransaction.getSender();
- String receiver = currentTransaction.getReceiver();
- int amount = currentTransaction.getTransferAmount();
-
- event.respondWith( "A transfer is already active. " + sender + " wants to transfer " + amount
- + " karma to " + receiver + ". " + receiver + ", please !accept or !reject this transfer."
- + " You have 30 seconds to respond." );
- }
- }
- }
-
- private boolean validAmount( int transferAmount, int senderKarma )
- {
- return ( transferAmount > 0 ) && ( senderKarma >= transferAmount );
- }
-
- private void startTransaction( GenericMessageEvent event, String sender, String receiver, int transferAmount )
- {
- currentTransaction = new KarmaTransaction( sender, receiver, transferAmount );
- event.respondWith( sender + " wants to transfer " + transferAmount + " karma to " + receiver + ". " + receiver
- + ", please !accept or !reject the transfer. You have 30 seconds to respond." );
-
- startTimer( event );
- }
-
- private void startTimer( final GenericMessageEvent event )
- {
- timer.submit( () -> {
- try
- {
- Thread.sleep( TIMEOUT );
- timeoutTimer( event );
- }
- catch ( InterruptedException e )
- {
- e.printStackTrace();
- }
- } );
- }
-
- private void timeoutTimer( final GenericMessageEvent event )
- {
- event.respondWith( "Transfer failed. No response was received within 30 seconds." );
-
- synchronized ( currentTransferMutex )
- {
- currentTransaction = null;
- }
- }
-
- private void stopTimer()
- {
- timer.shutdownNow();
- timer = Executors.newFixedThreadPool( 1 );
- }
-
- private void cancelTransfer( GenericMessageEvent event )
- {
- if ( !reportHasCurrentTransaction( event ) )
- return;
-
- String user = BaseNick.toBaseNick( ( event.getUser().getNick() ) );
- String sender = currentTransaction.getSender();
-
- if ( !sender.equals( user ) )
- {
- event.respondWith( "Only " + sender + " may cancel this transfer." );
- return;
- }
-
- // Cancel transfer
- stopTimer();
- synchronized ( currentTransferMutex )
- {
- currentTransaction = null;
- }
-
- event.respondWith( user + " has cancelled this transfer." );
- }
-
- private void acceptTransfer( GenericMessageEvent event )
- {
- if ( !reportHasCurrentTransaction( event ) )
- return;
-
- String user = BaseNick.toBaseNick( ( event.getUser().getNick() ) );
- String sender = currentTransaction.getSender();
- String receiver = currentTransaction.getReceiver();
- int amount = currentTransaction.getTransferAmount();
-
- if ( !receiver.equals( user ) )
- {
- event.respondWith( "Only " + receiver + " may accept this transfer." );
- return;
- }
-
- // Accept transfer
- stopTimer();
-
- KarmaDB.modNounKarma( receiver, amount );
- KarmaDB.modNounKarma( sender, -1 * amount );
-
- event.respondWith( "Transfer success! " + sender + " has transferred " + amount + " karma to " + receiver
- + "!" );
-
- // Reset
- synchronized ( currentTransferMutex )
- {
- currentTransaction = null;
- }
- }
-
- private void rejectTransfer( GenericMessageEvent event )
- {
- if ( !reportHasCurrentTransaction( event ) )
- return;
-
- String user = BaseNick.toBaseNick( ( event.getUser().getNick() ) );
- String sender = currentTransaction.getSender();
- String receiver = currentTransaction.getReceiver();
-
- if ( !receiver.equals( user ) )
- {
- event.respondWith( "Only " + receiver + " may reject this transfer." );
- return;
- }
-
- // Reject transfer
- stopTimer();
- synchronized ( currentTransferMutex )
- {
- currentTransaction = null;
- }
-
- event.respondWith( user + " has rejected " + sender + "'s transfer." );
- }
-
- private boolean reportHasCurrentTransaction( GenericMessageEvent event )
- {
- if ( currentTransaction == null )
- {
- event.respondWith( "No active transfer. To transfer karma enter '!transfer '." );
- return false;
- }
- return true;
- }
-
- private static class KarmaTransaction
- {
- private String sender;
-
- private String receiver;
-
- private final int transferAmount;
-
- KarmaTransaction( String senderNick, String receiverNick, int transferAmount )
- {
- if ( senderNick == null )
- throw new IllegalArgumentException( "senderNick can't be null" );
- if ( receiverNick == null )
- throw new IllegalArgumentException( "receiverNick can't be null" );
-
- this.sender = senderNick;
- this.receiver = receiverNick;
- this.transferAmount = transferAmount;
- }
-
- String getSender()
- {
- return sender;
- }
-
- String getReceiver()
- {
- return receiver;
- }
-
- int getTransferAmount()
- {
- return transferAmount;
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/LastMessageSed.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/LastMessageSed.java
deleted file mode 100644
index 6d6c7413..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/LastMessageSed.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.util.Colors;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class LastMessageSed
- extends ListenerAdapter
-{
-
- /**
- * Map with String key of IRC nick, to String value of the last line of text.
- */
- private final Map lastMessageMapByNick = new HashMap<>();
-
- /**
- * Syncronise access to lastMessageMapByNick on this.
- */
- private final Object lastMessageMapByNickMutex = new Object();
-
- /**
- * Matches a standard sed-like replace pattern (ex: s/foo/bar/), the forward slash divisor can be replaced with any
- * punctuation character. The given separator character cannot be used elsewhere. Optionally, following the last
- * slash, a user can write another user's name in order to modify that user's message.
- */
- private static final Pattern replacePattern =
- Pattern.compile( "^s(\\p{Punct})((?!\\1).+?)\\1((?!\\1).+?)(?:\\1(g|)(?:\\s+(\\S+)\\s*|)|)$" );
-
- /**
- * Say the last thing the person said, replaced as specified. Otherwise just record the line as the last thing the
- * person said.
- */
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher sedMatcher = replacePattern.matcher( text );
-
- String nick = event.getUser().getNick();
-
- if ( sedMatcher.matches() )
- {
- String correction = "Correction: ";
-
- // If the last group of the regex captures a non-null string, the user is fixing another user's message.
- if ( sedMatcher.group( 5 ) != null )
- {
- nick = sedMatcher.group( 5 );
- correction = nick + ", ftfy: ";
- }
-
- if ( lastMessageMapByNick.containsKey( nick ) )
- {
- String regexp = sedMatcher.group( 2 );
- String replacement = sedMatcher.group( 3 );
- String endFlag = sedMatcher.group( 4 );
-
- synchronized ( lastMessageMapByNickMutex )
- {
- String lastMessage = lastMessageMapByNick.get( nick );
-
- if ( !lastMessage.contains( regexp ) )
- {
- event.respondWith( "Wow. Seriously? Try subbing out a string that actually occurred. Do you even sed, bro?" );
- }
- else
- {
- String replacedMsg;
- String replacedMsgWHL;
-
- String replacementWHL = Colors.bold( replacement );
-
- // TODO: Probably can be simplified via method reference in Java 8
- if ( "g".equals( endFlag ) )
- {
- replacedMsg = lastMessage.replaceAll( regexp, replacement );
- replacedMsgWHL = lastMessage.replaceAll( regexp, replacementWHL );
- }
- else
- {
- replacedMsg = lastMessage.replaceFirst( regexp, replacement );
- replacedMsgWHL = lastMessage.replaceFirst( regexp, replacementWHL );
- }
-
- event.respondWith( correction + replacedMsgWHL );
- lastMessageMapByNick.put( nick, replacedMsg );
- }
- }
- }
- }
- else
- {
- synchronized ( lastMessageMapByNickMutex )
- {
- lastMessageMapByNick.put( nick, text );
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/LastSeen.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/LastSeen.java
deleted file mode 100644
index 5c84c200..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/LastSeen.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.db.LastSeenDB;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.Ignore;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.JoinEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import org.pircbotx.output.OutputIRC;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-
-public class LastSeen
- extends ListenerAdapter
-{
-
- private static TimeZone timeZone = TimeZone.getTimeZone( "America/Toronto" );
-
- private static DateFormat dateFormat = makeDateFormat();
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String userNick = BaseNick.toBaseNick( event.getUser().getNick() );
- String botNick = event.getBot().getNick();
-
- if ( !botNick.equals( userNick ) )
- {
- LastSeenDB.updateLastSeenTime( userNick );
- }
-
- if ( event instanceof JoinEvent && !botNick.equals( userNick ) && !Ignore.getOnJoin().contains( userNick ) )
- {
- longTimeNoSee( (JoinEvent) event, userNick );
- }
- }
-
- private void longTimeNoSee( JoinEvent event, String joiner )
- {
- OutputIRC outputQ = event.getBot().send();
- String replyTarget = event.getChannel().getName();
-
- long lastSeen = LastSeenDB.getLastSeenTime( joiner );
- long now = System.currentTimeMillis();
- long timeAgo = now - lastSeen;
-
- long daysAgo = TimeUnit.MILLISECONDS.toDays( timeAgo );
-
- if ( daysAgo > 30 )
- {
-
- String str = "Hi " + joiner + "! I last saw you " + daysAgo + " days ago at "
- + dateFormat.format( new Date( lastSeen ) ) + ". Long time, no see.";
- outputQ.message( replyTarget, str );
- }
-
- LastSeenDB.updateLastSeenTime( joiner );
- }
-
- private static DateFormat makeDateFormat()
- {
- SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mmX" );
- df.setTimeZone( timeZone );
- return df;
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Markov.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Markov.java
deleted file mode 100644
index 6c396bc9..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Markov.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.db.LogDB;
-import com.oldterns.vilebot.util.MangleNicks;
-import com.oldterns.vilebot.util.Zalgo;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.regex.Pattern;
-
-/**
- * Created by emmett on 12/08/15.
- */
-
-public class Markov
- extends ListenerAdapter
-{
-
- private Map> markovMap = new HashMap<>();
-
- private static final Pattern cmd = Pattern.compile( "^!speak$" );
-
- private static final Pattern gospelPattern = Pattern.compile( "^!gospel$" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
-
- String text = event.getMessage();
- boolean markovMap = cmd.matcher( text ).matches();
- boolean isGospel = gospelPattern.matcher( text ).matches();
-
- if ( markovMap || isGospel )
- {
- train();
- String phrase = generatePhrase();
- phrase = MangleNicks.mangleNicks( event, phrase );
- if ( isGospel )
- {
- phrase = Zalgo.generate( phrase );
- }
- event.respondWith( phrase );
- }
- }
-
- private void train()
- {
- String data = LogDB.getLog();
- fillMarkovMap( data );
- }
-
- private void fillMarkovMap( String data )
- {
- String[] words = data.split( "\\s+" );
-
- for ( int i = 0; i < words.length - 3; i++ )
- {
- String key = words[i] + " " + words[i + 1];
- String value = words[i + 2] + " " + words[i + 3];
-
- if ( key.equals( value ) )
- {
- continue;
- }
- else if ( markovMap.get( key ) != null )
- {
- markovMap.get( key ).add( value );
- }
- else
- {
- List valueList = new ArrayList<>();
- valueList.add( value );
- markovMap.put( key, valueList );
- }
- }
- }
-
- private String generatePhrase()
- {
- Random random = new Random();
- String key = getRandomKey( random );
- StringBuilder phrase = new StringBuilder();
-
- while ( key != null && phrase.length() < 1000 )
- {
- phrase.append( key ).append( " " );
- if ( shouldEnd( key ) )
- {
- break;
- }
- key = nextKey( key, random );
- }
-
- return phrase.toString().replace( "\n", " " );
- }
-
- private String nextKey( String key, Random random )
- {
- List valueList = markovMap.get( key );
- if ( valueList == null )
- {
- return null;
- }
-
- return valueList.get( random.nextInt( valueList.size() ) );
- }
-
- private String getRandomKey( Random random )
- {
- Object[] values = markovMap.keySet().toArray();
- return (String) values[random.nextInt( values.length )];
- }
-
- private boolean shouldEnd( String key )
- {
- return ( key.endsWith( "!" ) || key.endsWith( "?" ) || key.endsWith( "." ) );
- }
-
-}
\ No newline at end of file
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/News.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/News.java
deleted file mode 100644
index 5ab4b73e..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/News.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/**
- * Copyright (C) 2019 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.util.LimitCommand;
-import com.oldterns.vilebot.util.NewsParser;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.LinkedHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class News
- extends NewsParser
-{
- private static final Logger logger = LoggerFactory.getLogger( News.class );
-
- private static final String DEFAULT_CATEGORY = "toronto";
-
- private static final LinkedHashMap> newsFeedsByCategory = new LinkedHashMap<>();
- static
- {
- try
- {
- newsFeedsByCategory.put( "top",
- new ImmutablePair<>( "General",
- new URL( "https://rss.cbc.ca/lineup/topstories.xml" ) ) );
- newsFeedsByCategory.put( "world",
- new ImmutablePair<>( "General",
- new URL( "https://news.google.com/news/rss/headlines/section/topic/WORLD?ned=us&hl=en" ) ) );
- newsFeedsByCategory.put( "canada",
- new ImmutablePair<>( "General",
- new URL( "https://rss.cbc.ca/lineup/canada.xml" ) ) );
- newsFeedsByCategory.put( "usa",
- new ImmutablePair<>( "General",
- new URL( "http://feeds.reuters.com/Reuters/domesticNews" ) ) );
- newsFeedsByCategory.put( "britain",
- new ImmutablePair<>( "General",
- new URL( "http://feeds.bbci.co.uk/news/uk/rss.xml" ) ) );
- newsFeedsByCategory.put( "redhat",
- new ImmutablePair<>( "Open Source",
- new URL( "https://www.redhat.com/en/rss/blog/channel/red-hat-news" ) ) );
- newsFeedsByCategory.put( "fedora", new ImmutablePair<>( "Open Source",
- new URL( "http://fedoraplanet.org/rss20.xml" ) ) );
- newsFeedsByCategory.put( "openshift",
- new ImmutablePair<>( "Open Source",
- new URL( "https://blog.openshift.com/category/news/rss" ) ) );
- newsFeedsByCategory.put( "opensource",
- new ImmutablePair<>( "Open Source", new URL( "https://opensource.com/feed" ) ) );
- newsFeedsByCategory.put( "politics",
- new ImmutablePair<>( "Topics",
- new URL( "https://rss.cbc.ca/lineup/politics.xml" ) ) );
- newsFeedsByCategory.put( "business",
- new ImmutablePair<>( "Topics",
- new URL( "https://rss.cbc.ca/lineup/business.xml" ) ) );
- newsFeedsByCategory.put( "health",
- new ImmutablePair<>( "Topics",
- new URL( "https://rss.cbc.ca/lineup/health.xml" ) ) );
- newsFeedsByCategory.put( "arts",
- new ImmutablePair<>( "Topics", new URL( "https://rss.cbc.ca/lineup/arts.xml" ) ) );
- newsFeedsByCategory.put( "tech",
- new ImmutablePair<>( "Topics",
- new URL( "https://rss.cbc.ca/lineup/technology.xml" ) ) );
- newsFeedsByCategory.put( "offbeat",
- new ImmutablePair<>( "Topics",
- new URL( "https://rss.cbc.ca/lineup/offbeat.xml" ) ) );
- newsFeedsByCategory.put( "indigenous",
- new ImmutablePair<>( "Topics",
- new URL( "https://www.cbc.ca/cmlink/rss-cbcaboriginal" ) ) );
- newsFeedsByCategory.put( "sports",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports.xml" ) ) );
- newsFeedsByCategory.put( "mlb",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-mlb.xml" ) ) );
- newsFeedsByCategory.put( "nba",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-nba.xml" ) ) );
- newsFeedsByCategory.put( "cfl",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-cfl.xml" ) ) );
- newsFeedsByCategory.put( "nfl",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-nfl.xml" ) ) );
- newsFeedsByCategory.put( "nhl",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-nhl.xml" ) ) );
- newsFeedsByCategory.put( "soccer",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-soccer.xml" ) ) );
- newsFeedsByCategory.put( "curling",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-curling.xml" ) ) );
- newsFeedsByCategory.put( "skating",
- new ImmutablePair<>( "Sports",
- new URL( "https://rss.cbc.ca/lineup/sports-figureskating.xml" ) ) );
- newsFeedsByCategory.put( "bc",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-britishcolumbia.xml" ) ) );
- newsFeedsByCategory.put( "kamloops",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-kamloops.xml" ) ) );
- newsFeedsByCategory.put( "calgary",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-calgary.xml" ) ) );
- newsFeedsByCategory.put( "edmonton",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-edmonton.xml" ) ) );
- newsFeedsByCategory.put( "saskatchewan",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-saskatchewan.xml" ) ) );
- newsFeedsByCategory.put( "saskatoon",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-saskatoon.xml" ) ) );
- newsFeedsByCategory.put( "manitoba",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-manitoba.xml" ) ) );
- newsFeedsByCategory.put( "thunderbay",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-thunderbay.xml" ) ) );
- newsFeedsByCategory.put( "sudbury",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-sudbury.xml" ) ) );
- newsFeedsByCategory.put( "windsor",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-windsor.xml" ) ) );
- newsFeedsByCategory.put( "london",
- new ImmutablePair<>( "Regional",
- new URL( "https://www.cbc.ca/cmlink/rss-canada-london" ) ) );
- newsFeedsByCategory.put( "waterloo",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-kitchenerwaterloo.xml" ) ) );
- newsFeedsByCategory.put( "toronto",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-toronto.xml" ) ) );
- newsFeedsByCategory.put( "hamilton",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-hamiltonnews.xml" ) ) );
- newsFeedsByCategory.put( "montreal",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-montreal.xml" ) ) );
- newsFeedsByCategory.put( "newbrunswick",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-newbrunswick.xml" ) ) );
- newsFeedsByCategory.put( "pei",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-pei.xml" ) ) );
- newsFeedsByCategory.put( "novascotia",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-novascotia.xml" ) ) );
- newsFeedsByCategory.put( "newfoundland",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-newfoundland.xml" ) ) );
- newsFeedsByCategory.put( "north",
- new ImmutablePair<>( "Regional",
- new URL( "https://rss.cbc.ca/lineup/canada-north.xml" ) ) );
- }
- catch ( MalformedURLException e )
- {
- logger.error( "Error loading news URLs" );
- throw new RuntimeException( e );
- }
- }
-
- private static final Pattern NEWS_PATTERN = Pattern.compile( "^!news(?: ([a-zA-Z]+)|)" );
-
- private static final Pattern NEWS_HELP_PATTERN = Pattern.compile( "^!news help" );
-
- private final String HELP_MESSAGE = generateHelpMessage();
-
- private final String HELP_COMMAND = "'!news help'";
-
- public static LimitCommand limitCommand = new LimitCommand();
-
- private static final String RESTRICTED_CHANNEL = Vilebot.getConfig().get( "ircChannel1" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = NEWS_PATTERN.matcher( text );
- Matcher helpMatcher = NEWS_HELP_PATTERN.matcher( text );
-
- if ( helpMatcher.matches() )
- {
- for ( String line : HELP_MESSAGE.split( "\n" ) )
- {
- event.respondPrivateMessage( line );
- }
- }
- else if ( matcher.matches() )
- {
- currentNews( event, matcher, newsFeedsByCategory, DEFAULT_CATEGORY, HELP_COMMAND, limitCommand,
- RESTRICTED_CHANNEL, logger );
- }
- }
-
- @Override
- protected String generateHelpMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "News Categories (example: !news toronto):" );
-
- String prevGenre = null;
-
- for ( String category : newsFeedsByCategory.keySet() )
- {
- String currentGenre = newsFeedsByCategory.get( category ).getLeft();
-
- if ( !currentGenre.equals( prevGenre ) )
- {
- sb.append( "\n" );
- sb.append( " " + currentGenre + ":" );
- prevGenre = currentGenre;
- }
-
- sb.append( " { " + category + " }" );
- }
-
- return sb.toString();
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Omgword.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Omgword.java
deleted file mode 100644
index 51a4b96e..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Omgword.java
+++ /dev/null
@@ -1,225 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.db.KarmaDB;
-import com.oldterns.vilebot.util.BaseNick;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.Random;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Based off of the omgword game from CasinoBot: http://casinobot.codeplex.com/
- */
-public class Omgword
- extends ListenerAdapter
-{
-
- private static final Pattern QUESTION_PATTERN = Pattern.compile( "!omgword" );
-
- private static final Pattern ANSWER_PATTERN = Pattern.compile( "!answer (.*)" );
-
- private static final String WORD_LIST_PATH = Vilebot.getConfig().get( "OmgwordList" );
-
- private static final String OMGWORD_CHANNEL = Vilebot.getConfig().get( "OmgwordChannel" );
-
- private ArrayList words = loadWords();
-
- private OmgwordGame currentGame;
-
- private String word;
-
- private String scrambled;
-
- private static final Random random = new SecureRandom();
-
- private static final long TIMEOUT = 30000L;
-
- private static ExecutorService timer = Executors.newScheduledThreadPool( 1 );
-
- @Override
- public void onMessage( MessageEvent event )
- {
- String text = event.getMessage();
- Matcher questionMatcher = QUESTION_PATTERN.matcher( text );
- Matcher answerMatcher = ANSWER_PATTERN.matcher( text );
- if ( questionMatcher.matches() && shouldStartGame( event ) )
- {
- startGame( event );
- }
- else if ( answerMatcher.matches() && shouldStartGame( event ) )
- {
- String answer = answerMatcher.group( 1 );
- finishGame( event, answer );
- }
- }
-
- private boolean shouldStartGame( MessageEvent event )
- {
- String actualChannel = event.getChannel().getName();
-
- if ( OMGWORD_CHANNEL.equalsIgnoreCase( actualChannel ) )
- {
- return true;
- }
- event.respondWith( "To play Omgword join: " + OMGWORD_CHANNEL );
- return false;
- }
-
- private synchronized void startGame( MessageEvent event )
- {
- if ( currentGame != null )
- {
- event.respondWith( currentGame.getAlreadyPlayingString() );
- }
- else
- {
- currentGame = new OmgwordGame();
- for ( String msg : currentGame.getIntroString() )
- {
- event.respondWith( msg );
- }
- startTimer( event );
- }
- }
-
- private void startTimer( final MessageEvent event )
- {
- timer.submit( () -> {
- try
- {
- Thread.sleep( TIMEOUT );
- timeoutTimer( event );
- }
- catch ( InterruptedException e )
- {
- e.printStackTrace();
- }
- } );
- }
-
- private void timeoutTimer( MessageEvent event )
- {
- String message = currentGame.getTimeoutString();
- event.respondWith( message );
- currentGame = null;
- }
-
- private void stopTimer()
- {
- timer.shutdownNow();
- timer = Executors.newFixedThreadPool( 1 );
- }
-
- private synchronized void finishGame( MessageEvent event, String answer )
- {
- String answerer = BaseNick.toBaseNick( Objects.requireNonNull( event.getUser() ).getNick() );
- if ( currentGame != null )
- {
- if ( currentGame.isCorrect( answer ) )
- {
- stopTimer();
- event.respondWith( String.format( "Congrats %s, you win %d karma!", answerer,
- currentGame.getStakes() ) );
- KarmaDB.modNounKarma( answerer, currentGame.getStakes() );
- currentGame = null;
- }
- else
- {
- event.respondWith( String.format( "Sorry %s! That is incorrect, you lose %d karma.", answerer,
- currentGame.getStakes() ) );
- KarmaDB.modNounKarma( answerer, -1 * currentGame.getStakes() );
- }
- }
- else
- {
- event.respondWith( "No active game. Start a new one with !omgword" );
- }
- }
-
- private ArrayList loadWords()
- {
- try
- {
- ArrayList words = new ArrayList<>();
- List lines = Files.readAllLines( Paths.get( WORD_LIST_PATH ), Charset.forName( "UTF-8" ) );
- for ( String line : lines )
- {
- words.addAll( Arrays.asList( line.split( " " ) ) );
- }
- return words;
- }
- catch ( Exception e )
- {
- System.exit( 1 );
- }
- return null;
- }
-
- private class OmgwordGame
- {
-
- String getAlreadyPlayingString()
- {
- return "A game is already in progress! Your word is: " + scrambled;
- }
-
- String[] getIntroString()
- {
- word = "";
- int index = random.nextInt( words.size() - 1 );
- word = words.get( index );
- scrambled = word;
- char[] chars = words.get( index ).toCharArray();
- while ( scrambled.equals( word ) )
- {
- shuffleArray( chars );
- scrambled = new String( chars );
- }
- return new String[] { "Welcome to omgword!", "For " + getStakes() + " karma:", scrambled,
- "30 seconds on the clock." };
- }
-
- boolean isCorrect( String answer )
- {
- return answer.equals( word );
- }
-
- String getTimeoutString()
- {
- return "Game over! The correct answer was: " + word;
- }
-
- int getStakes()
- {
- return (int) Math.ceil( word.length() >> 1 );
- }
- }
-
- private static void shuffleArray( char[] array )
- {
- char temp;
- int index;
- Random random = new Random();
- for ( int i = array.length - 1; i > 0; i-- )
- {
- index = random.nextInt( i + 1 );
- temp = array[index];
- array[index] = array[i];
- array[i] = temp;
- }
- }
-
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Ops.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Ops.java
deleted file mode 100644
index 4be04a25..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Ops.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- Copyright (C) 2013 Oldterns
-
- This file may be modified and distributed under the terms
- of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.db.GroupDB;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.Ignore;
-import org.pircbotx.Configuration;
-import org.pircbotx.User;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.JoinEvent;
-
-public class Ops
- extends ListenerAdapter
-{
-
- @Override
- public void onJoin( final JoinEvent event )
- {
- String joiner = event.getUser().getNick();
- String joinerBaseNick = BaseNick.toBaseNick( joiner );
-
- if ( !Ignore.getAutoOp().contains( event.getChannel().getName() )
- && event.getChannel().isOp( event.getBot().getUserBot() ) )
- {
- if ( event.getBot().getNick().equals( joiner ) )
- {
- for ( User user : event.getChannel().getNormalUsers() )
- {
- if ( GroupDB.isOp( BaseNick.toBaseNick( user.getNick() ) ) )
- {
- event.getChannel().send().op( new Configuration.BotFactory().createUserHostmask( event.getBot(),
- user.getHostmask() ) );
- }
- }
- }
- else if ( GroupDB.isOp( joinerBaseNick ) )
- {
- event.getChannel().send().op( event.getUserHostmask() );
- }
-
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/QuotesAndFacts.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/QuotesAndFacts.java
deleted file mode 100644
index db996a37..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/QuotesAndFacts.java
+++ /dev/null
@@ -1,631 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.db.ChurchDB;
-import com.oldterns.vilebot.db.QuoteFactDB;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.Ignore;
-import com.oldterns.vilebot.util.MangleNicks;
-import com.oldterns.vilebot.util.StringUtil;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.JoinEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-public class QuotesAndFacts
- extends ListenerAdapter
-{
- private static final Pattern nounPattern = Pattern.compile( "\\S+" );
-
- private static final Pattern addPattern = Pattern.compile( "^!(fact|quote)add (" + nounPattern + ") (.+)$" );
-
- private static final Pattern dumpPattern = Pattern.compile( "^!(fact|quote)dump (" + nounPattern + ")\\s*$" );
-
- private static final Pattern randomPattern = Pattern.compile( "^!(fact|quote)random5 (" + nounPattern + ")\\s*$" );
-
- private static final Pattern numPattern = Pattern.compile( "^!(fact|quote)number (" + nounPattern + ")\\s*$" );
-
- private static final Pattern queryPattern =
- Pattern.compile( "^!(fact|quote) (" + nounPattern + ")( !jaziz)?\\s*$" );
-
- // "( !jaziz)" is not included in searchPattern because it is handled in factQuoteSearch method
- private static final Pattern searchPattern = Pattern.compile( "^!(fact|quote)search (" + nounPattern + ") (.*)$" );
-
- private static final Random random = new Random();
-
- // cache fact and quote dump links
- private Map dumpCache = new HashMap<>();
-
- // update cache when new quotes/facts added
- private Map dumpSize = new HashMap<>();
-
- private static final String PASTEBIN_API_URL = Vilebot.getConfig().get( "pastebinApiUrl" );
-
- @Override
- public void onJoin( final JoinEvent event ) // announce fact or quote on join
- {
- String baseNick = BaseNick.toBaseNick( Objects.requireNonNull( event.getUser() ).getNick() );
- if ( !Ignore.getOnJoin().contains( baseNick ) )
- {
- if ( random.nextBoolean() )
- {
- if ( !replyWithQuote( baseNick, event, false ) )
- replyWithFact( baseNick, event, false );
- }
- else
- {
- if ( !replyWithFact( baseNick, event, false ) )
- replyWithQuote( baseNick, event, false );
- }
- }
- }
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
-
- Matcher addMatcher = addPattern.matcher( text );
- Matcher dumpMatcher = dumpPattern.matcher( text );
- Matcher factQuoteRandomDumpMatcher = randomPattern.matcher( text );
- Matcher factQuoteNumMatcher = numPattern.matcher( text );
- Matcher queryMatcher = queryPattern.matcher( text );
- Matcher searchMatcher = searchPattern.matcher( text );
-
- if ( addMatcher.matches() )
- factQuoteAdd( event, addMatcher );
- if ( dumpMatcher.matches() )
- factQuoteDump( event, dumpMatcher );
- if ( factQuoteRandomDumpMatcher.matches() )
- factQuoteRandomDump( event, factQuoteRandomDumpMatcher );
- if ( factQuoteNumMatcher.matches() )
- factQuoteNum( event, factQuoteNumMatcher );
- if ( queryMatcher.matches() )
- factQuoteQuery( event, queryMatcher );
- if ( searchMatcher.matches() )
- factQuoteSearch( event, searchMatcher );
- }
-
- private void factQuoteAdd( GenericMessageEvent event, Matcher addMatcher )
- {
- String mode = addMatcher.group( 1 );
- String noun = BaseNick.toBaseNick( addMatcher.group( 2 ) );
- String text = addMatcher.group( 3 );
- String sender = BaseNick.toBaseNick( event.getUser().getNick() );
-
- if ( !sender.equals( noun ) )
- {
- text = trimChars( text, " '\"" );
-
- if ( "fact".equals( mode ) )
- {
- QuoteFactDB.addFact( noun, text );
- event.respondWith( formatFactReply( noun, text ) );
- }
- else
- {
- QuoteFactDB.addQuote( noun, text );
- event.respondWith( formatQuoteReply( noun, text ) );
- }
- }
- else
- {
- event.respondWith( StringUtil.capitalizeFirstLetter( mode )
- + "s from yourself are both terrible and uninteresting." );
- }
- }
-
- private void factQuoteDump( GenericMessageEvent event, Matcher dumpMatcher )
- {
- String mode = dumpMatcher.group( 1 );
- String queried = BaseNick.toBaseNick( dumpMatcher.group( 2 ) );
-
- if ( mode.equals( "fact" ) )
- {
- Set allFacts = QuoteFactDB.getFacts( queried );
- if ( allFacts.isEmpty() )
- {
- event.respondWith( queried + " has no facts." );
- }
- else
- {
- String dumpKey = "fact" + queried;
- String response;
- if ( allFacts.size() != dumpSize.getOrDefault( dumpKey, 0 ) )
- {
- try
- {
- String title = queried + "'s facts";
- String pasteLink = dumpToPastebin( title, allFacts );
- dumpCache.put( dumpKey, pasteLink );
- dumpSize.put( dumpKey, allFacts.size() );
- response = "factdump for " + queried + ": " + pasteLink;
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- response = "Error creating pastebin link";
- }
- }
- else
- {
- String pasteLink = dumpCache.get( dumpKey );
- response = "factdump for " + queried + ": " + pasteLink;
- }
- event.respondWith( response );
- }
- }
- else
- {
- Set allQuotes = QuoteFactDB.getQuotes( queried );
- if ( allQuotes.isEmpty() )
- {
- event.respondWith( queried + " has no quotes." );
- }
- else
- {
- String dumpKey = "quote" + queried;
- String response;
- if ( allQuotes.size() != dumpSize.getOrDefault( dumpKey, 0 ) )
- {
- try
- {
- String title = queried.trim() + "'s quotes";
- String pasteLink = dumpToPastebin( title, allQuotes );
- dumpCache.put( dumpKey, pasteLink );
- dumpSize.put( dumpKey, allQuotes.size() );
- response = "quotedump for " + queried + ": " + pasteLink;
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- response = "Error creating pastebin link";
- }
- }
- else
- {
- String pasteLink = dumpCache.get( dumpKey );
- response = "quotedump for " + queried + ": " + pasteLink;
- }
- event.respondWith( response );
- }
- }
- }
-
- private String dumpToPastebin( String title, Set allQuotes )
- throws Exception
- {
- StringBuilder sb = new StringBuilder();
- for ( String quote : allQuotes )
- {
- sb.append( URLEncoder.encode( quote + "\n", StandardCharsets.UTF_8 ) );
- }
-
- URL url = new URL( PASTEBIN_API_URL );
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setDoOutput( true );
- conn.setRequestMethod( "POST" );
- conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
- conn.setInstanceFollowRedirects( false );
- String input = "format=text&code2=" + title + "\n\n" + sb.toString() + "&poster=vilebot&paste=Send&expiry=m";
-
- OutputStream os = conn.getOutputStream();
- os.write( input.getBytes() );
- os.flush();
- assert conn.getResponseCode() == 302;
- return conn.getHeaderField( "Location" );
- }
-
- private void factQuoteRandomDump( GenericMessageEvent event, Matcher factQuoteRandomDumpMatcher )
- {
- String mode = factQuoteRandomDumpMatcher.group( 1 );
- String queried = BaseNick.toBaseNick( factQuoteRandomDumpMatcher.group( 2 ) );
-
- if ( "fact".equals( mode ) )
- {
- Long factsLength = QuoteFactDB.getFactsLength( queried );
- if ( factsLength == 0 )
- {
- event.respondPrivateMessage( queried + " has no facts." );
- }
- else if ( factsLength <= 5 )
- {
- Set allFacts = QuoteFactDB.getFacts( queried );
- for ( String fact : allFacts )
- {
- event.respondPrivateMessage( formatFactReply( queried, fact ) );
- }
- }
- else
- {
- List randomFacts = QuoteFactDB.getRandFacts( queried );
- for ( String fact : randomFacts )
- {
- event.respondPrivateMessage( formatFactReply( queried, fact ) );
- }
- }
- }
- else
- {
- Long quotesLength = QuoteFactDB.getQuotesLength( queried );
- if ( quotesLength == 0 )
- {
- event.respondPrivateMessage( queried + " has no quotes." );
- }
- else if ( quotesLength <= 5 )
- {
- Set allQuotes = QuoteFactDB.getQuotes( queried );
- for ( String quote : allQuotes )
- {
- event.respondPrivateMessage( formatQuoteReply( queried, quote ) );
- }
- }
- else
- {
- List randomQuote = QuoteFactDB.getRandQuotes( queried );
- for ( String quote : randomQuote )
- {
- event.respondPrivateMessage( formatQuoteReply( queried, quote ) );
- }
- }
- }
- }
-
- private void factQuoteNum( GenericMessageEvent event, Matcher factQuoteNumMatcher )
- {
- String mode = factQuoteNumMatcher.group( 1 );
- String queried = BaseNick.toBaseNick( factQuoteNumMatcher.group( 2 ) );
- if ( "fact".equals( mode ) )
- {
- Long factsLength = QuoteFactDB.getFactsLength( queried );
- if ( factsLength == 0 )
- {
- event.respondWith( queried + " has no facts." );
- }
- else
- {
- event.respondWith( queried + " has " + factsLength + " facts." );
- }
- }
- else
- {
- Long quotesLength = QuoteFactDB.getQuotesLength( queried );
- if ( quotesLength == 0 )
- {
- event.respondWith( queried + " has no quotes." );
- }
- else
- {
- event.respondWith( queried + " has " + quotesLength + " quotes." );
- }
- }
- }
-
- private void factQuoteQuery( GenericMessageEvent event, Matcher queryMatcher )
- {
- String mode = queryMatcher.group( 1 );
- String noun = BaseNick.toBaseNick( queryMatcher.group( 2 ) );
-
- // check if quote/fact needs to be piped to jaziz
- boolean jaziz = event.getMessage().lastIndexOf( "!jaziz" ) >= 0;
-
- if ( "fact".equals( mode ) )
- {
- if ( !replyWithFact( noun, event, jaziz ) )
- {
- event.respondWith( noun + " has no facts." );
- }
- }
- else
- {
- if ( !replyWithQuote( noun, event, jaziz ) )
- {
- event.respondWith( noun + " has no quotes." );
- }
- }
- }
-
- private void factQuoteSearch( GenericMessageEvent event, Matcher searchMatcher )
- {
- String mode = searchMatcher.group( 1 );
- String noun = BaseNick.toBaseNick( searchMatcher.group( 2 ) );
- String regex = searchMatcher.group( 3 );
-
- // check if quote/fact needs to be piped to jaziz
- int jazizIdx = regex.lastIndexOf( "!jaziz" );
- boolean jaziz = jazizIdx >= 0;
- if ( jaziz )
- {
- regex = regex.substring( 0, jazizIdx - 1 );
- }
-
- try
- {
- // Case insensitive added automatically, use (?-i) in a message to re-enable case sensitivity
- Pattern pattern = Pattern.compile( "(?i)" + regex );
-
- if ( "fact".equals( mode ) )
- {
- Set texts = QuoteFactDB.getFacts( noun );
- if ( texts != null )
- {
- String randomMatch = regexSetSearch( texts, pattern );
- if ( randomMatch != null )
- {
- randomMatch = MangleNicks.mangleNicks( event, randomMatch );
- if ( jaziz )
- {
- try
- {
- event.respondWith( formatFactReply( noun, Jaziz.jazizify( randomMatch ) ) );
- }
- catch ( Exception e )
- {
- event.respondWith( "eeeh" );
- e.printStackTrace();
- }
- }
- else
- {
- event.respondWith( formatFactReply( noun, randomMatch ) );
- }
- }
- else
- {
- event.respondWith( noun + " has no matching facts." );
- }
- }
- else
- {
- event.respondWith( noun + " has no facts." );
- }
- }
- else
- {
- Set texts = QuoteFactDB.getQuotes( noun );
- if ( texts != null )
- {
- String randomMatch = regexSetSearch( texts, pattern );
- if ( randomMatch != null )
- {
- randomMatch = MangleNicks.mangleNicks( event, randomMatch );
- if ( jaziz )
- {
- try
- {
- event.respondWith( formatQuoteReply( noun, Jaziz.jazizify( randomMatch ) ) );
- }
- catch ( Exception e )
- {
- event.respondWith( "eeeh" );
- e.printStackTrace();
- }
- }
- else
- {
- event.respondWith( formatQuoteReply( noun, randomMatch ) );
- }
- }
- else
- {
- event.respondWith( noun + " has no matching quotes." );
- }
- }
- else
- {
- event.respondWith( noun + " has no quotes." );
- }
- }
- }
- catch ( PatternSyntaxException e )
- {
- event.respondWith( "Syntax error in regex pattern" );
- }
- }
-
- private static String regexSetSearch( Set texts, Pattern pattern )
- {
- List matchingTexts = new LinkedList<>();
-
- for ( String text : texts )
- {
- Matcher matcher = pattern.matcher( text );
- if ( matcher.find() )
- {
- matchingTexts.add( text );
- }
- }
-
- int matchCount = matchingTexts.size();
- if ( matchCount > 0 )
- {
- int selection = random.nextInt( matchCount );
- return matchingTexts.get( selection );
- }
- else
- {
- return null;
- }
- }
-
- private boolean replyWithFact( String noun, GenericMessageEvent event, boolean jaziz )
- {
- String replyText = getReplyFact( noun, jaziz );
- if ( replyText != null )
- {
- replyText = MangleNicks.mangleNicks( event, replyText );
- replyText = formatFactReply( getTitle( noun ), replyText );
- event.respondWith( replyText );
- return true;
- }
- else
- {
- return false;
- }
- }
-
- private boolean replyWithFact( String noun, JoinEvent event, boolean jaziz )
- {
- String replyText = getReplyFact( noun, jaziz );
- if ( replyText != null )
- {
- replyText = MangleNicks.mangleNicks( event, replyText );
- replyText = formatFactReply( getTitle( noun ), replyText );
- event.getBot().send().message( event.getChannel().getName(), replyText );
- return true;
- }
- else
- {
- return false;
- }
- }
-
- private String getReplyFact( String noun, boolean jaziz )
- {
- String text = QuoteFactDB.getRandFact( noun );
- if ( text == null )
- {
- return null;
- }
- if ( jaziz )
- {
- try
- {
- text = Jaziz.jazizify( text );
- }
- catch ( Exception e )
- {
- text = "eeeh";
- e.printStackTrace();
- }
- }
- return text;
- }
-
- private String formatFactReply( String noun, String fact )
- {
- return noun + " " + fact;
- }
-
- private boolean replyWithQuote( String noun, GenericMessageEvent event, boolean jaziz )
- {
- String replyText = getReplyQuote( noun, jaziz );
- if ( replyText != null )
- {
- replyText = MangleNicks.mangleNicks( event, replyText );
- replyText = formatQuoteReply( getTitle( noun ), replyText );
- event.respondWith( replyText );
- return true;
- }
- else
- {
- return false;
- }
- }
-
- private boolean replyWithQuote( String noun, JoinEvent event, boolean jaziz )
- {
- String replyText = getReplyQuote( noun, jaziz );
- if ( replyText != null )
- {
- replyText = MangleNicks.mangleNicks( event, replyText );
- replyText = formatQuoteReply( getTitle( noun ), replyText );
- event.getBot().send().message( event.getChannel().getName(), replyText );
- return true;
- }
- else
- {
- return false;
- }
- }
-
- private String getReplyQuote( String noun, boolean jaziz )
- {
- String text = QuoteFactDB.getRandQuote( noun );
- if ( text == null )
- {
- return null;
- }
- if ( jaziz )
- {
- try
- {
- text = Jaziz.jazizify( text );
- }
- catch ( Exception e )
- {
- text = "eeeh";
- e.printStackTrace();
- }
- }
- return text;
- }
-
- private String formatQuoteReply( String noun, String quote )
- {
- return noun + " once said, \"" + quote + "\".";
- }
-
- private String getTitle( String noun )
- {
- if ( ChurchDB.isTopDonor( noun ) )
- {
- String title = ChurchDB.getDonorTitle( noun );
- if ( title.trim().length() > 0 )
- {
- noun = title;
- }
- }
- return noun;
- }
-
- /**
- * Removes all specified leading and trailing characters in the array charsToRemove.
- *
- * @param input The string to process
- * @param charsToRemove All characters to remove, treated as a set
- * @return A copy of the input String with the characters removed
- */
- private String trimChars( String input, String charsToRemove )
- {
- char[] value = input.toCharArray();
- char[] rmChars = charsToRemove.toCharArray();
- Arrays.sort( rmChars );
-
- int len = value.length;
- int st = 0;
-
- while ( ( st < len ) && ( Arrays.binarySearch( rmChars, value[st] ) >= 0 ) )
- {
- st++;
- }
- while ( ( st < len ) && ( Arrays.binarySearch( rmChars, value[len - 1] ) >= 0 ) )
- {
- len--;
- }
-
- return input.substring( st, len );
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/RemindMe.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/RemindMe.java
deleted file mode 100644
index 30335e44..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/RemindMe.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.util.BaseNick;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-//@HandlerContainer
-public class RemindMe
- extends ListenerAdapter
-{
-
- private static final String timeFormat = "(\\d+\\w*)";
-
- private static final Pattern remindMePattern = Pattern.compile( "^!remindme (.+) " + timeFormat );
-
- private final String INVALID_TYPE_ERROR =
- "The time type given is not valid (use d for day, m for month, s for second)";
-
- private final String NO_TYPE_ERROR = "There was no type given for the time (use d/m/s)";
-
- private final String TIME_TOO_LARGE_ERROR = "The value of time given is greater than the maximum Integer value";
-
- private final String TOO_MANY_REMINDERS_ERROR =
- "There is a limit of 10 reminders, please wait until one reminder ends to set a new one.";
-
- private final String TIME_IS_OKAY = "Given time input is okay";
-
- private String timeError = TIME_IS_OKAY;
-
- private static Map userReminders = new HashMap<>();
-
- private final int MAX_REMINDERS = 10;
-
- // @Handler
- @Override
- public void onGenericMessage( GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = remindMePattern.matcher( text );
-
- if ( matcher.matches() )
- {
- String message = matcher.group( 1 );
- String time = matcher.group( 2 );
- String creator = BaseNick.toBaseNick( event.getUser().getNick() );
- if ( !userReminders.containsKey( creator ) )
- {
- userReminders.put( creator, 0 );
- }
- Calendar timerTime = getTimerTime( time, creator );
- if ( timerTime == null )
- {
- event.respondPrivateMessage( String.format( "The given time of %s is invalid. The cause is %s.", time,
- timeError ) );
- }
- else
- {
- Timer timer = new Timer();
- timer.schedule( createTimerTask( event, message, creator ), timerTime.getTime() );
- event.respondPrivateMessage( "Created reminder for " + timerTime.getTime() );
- int amountOfReminders = userReminders.get( creator );
- amountOfReminders++;
- userReminders.put( creator, amountOfReminders );
- }
- }
- }
-
- private TimerTask createTimerTask( final GenericMessageEvent event, final String message, final String creator )
- {
- return new TimerTask()
- {
- @Override
- public void run()
- {
- event.respondPrivateMessage( "This is your reminder that you should: " + message );
- int amountOfReminders = userReminders.get( creator );
- amountOfReminders--;
- userReminders.put( creator, amountOfReminders );
- }
- };
- }
-
- private Calendar getTimerTime( final String time, final String creator )
- {
- Calendar calendar = Calendar.getInstance();
- verifyTime( time, creator );
- if ( !timeError.equals( TIME_IS_OKAY ) )
- {
- return null;
- }
- int timeValue = Integer.parseInt( time.substring( 0, time.length() - 1 ) );
- switch ( time.substring( time.length() - 1 ) )
- {
- case "d":
- calendar.add( Calendar.DAY_OF_MONTH, timeValue );
- case "m":
- calendar.add( Calendar.MINUTE, timeValue );
- case "s":
- calendar.add( Calendar.SECOND, timeValue );
- }
- return calendar;
- }
-
- private void verifyTime( final String time, final String creator )
- {
- String type = time.substring( time.length() - 1 );
- if ( !type.equals( "d" ) && !type.equals( "m" ) && !type.equals( "s" ) )
- {
- if ( isNumeric( time ) )
- {
- timeError = NO_TYPE_ERROR;
- }
- else
- {
- timeError = INVALID_TYPE_ERROR;
- }
- return;
- }
- try
- {
- String givenTime = time.substring( 0, time.length() - 1 );
- Integer.valueOf( givenTime );
- }
- catch ( Exception e )
- {
- timeError = TIME_TOO_LARGE_ERROR;
- return;
- }
- if ( userReminders.get( creator ) == MAX_REMINDERS )
- {
- timeError = TOO_MANY_REMINDERS_ERROR;
- return;
- }
- timeError = TIME_IS_OKAY;
- }
-
- private boolean isNumeric( final String time )
- {
- try
- {
- Integer.valueOf( time );
- }
- catch ( Exception e )
- {
- return false;
- }
- return true;
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/RockPaperScissors.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/RockPaperScissors.java
deleted file mode 100644
index fe7d760d..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/RockPaperScissors.java
+++ /dev/null
@@ -1,351 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.util.BaseNick;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.events.PrivateMessageEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.Random;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Created by Christopher Chianelli on 27/08/17. Thanks to Josh Matsuoka for helping squashing bugs RockPaperScissors
- * implementation based off of Trivia.java
- */
-public class RockPaperScissors
- extends ListenerAdapter
-{
-
- private static final String RPS_CHANNEL = Vilebot.getConfig().get( "rpsChannel" );
-
- private static final int DEFAULT_STAKE = 10;
-
- private static final String RED = "\u000304";
-
- private static final String RESET = "\u000f";
-
- private static final String BLUE = "\u000302";
-
- private static final String GREEN = "\u000303";
-
- private static final Pattern rpsPattern = Pattern.compile( "^!rps (\\S*)(.*)" );
-
- private static final Pattern answerPattern = Pattern.compile( "^!(rock|paper|scissors)" );
-
- private static final Pattern cancelPattern = Pattern.compile( "^!rpscancel" );
-
- private static final Pattern rulesPattern = Pattern.compile( "^!rpsrules" );
-
- private static RPSGame currGame = null;
-
- private static class RPSGame
- {
- private static final String[] answers = { "rock", "paper", "scissors" };
-
- private String callerNick;
-
- private String daredNick;
-
- private String callerAnswer;
-
- private String daredAnswer;
-
- private String daredThing;
-
- private GenericMessageEvent event;
-
- Random rand = new Random();
-
- RPSGame( String callerNick, String daredNick, String daredThing, GenericMessageEvent event )
- {
- this.callerNick = callerNick;
- this.daredNick = daredNick;
- this.callerAnswer = null;
- this.daredAnswer = null;
- this.daredThing = daredThing;
- this.event = event;
- rand = new Random();
- if ( daredNick.equals( event.getBot().getNick() ) )
- {
- this.daredAnswer = answers[rand.nextInt( answers.length )];
- }
- }
-
- private String getCaller()
- {
- return callerNick;
- }
-
- private String getDared()
- {
- return daredNick;
- }
-
- private GenericMessageEvent getEvent()
- {
- return event;
- }
-
- private String getRPSIntro()
- {
- String start = RED + callerNick + " dared " + daredNick + " to a Rock Paper Scissors match";
- if ( daredThing != null )
- {
- return start + " for " + daredThing + "!" + RESET + "\n";
- }
- return start + "!" + RESET + "\n";
- }
-
- private String getRPSOutro()
- {
- String start =
- RED + callerNick + " used " + callerAnswer + ", " + daredNick + " used " + daredAnswer + ", ";
- if ( null != getWinner() )
- {
- return start + getWinner() + " wins" + ( ( null != daredThing ) ? " " + daredThing : "" ) + "!";
- }
- return start + "no one wins!";
- }
-
- private String alreadyPlaying()
- {
- return "A current game is already in progress.\n" + getRPSIntro();
- }
-
- private boolean setSubmission( String contestant, String answer )
- throws Exception
- {
- if ( contestant.equals( callerNick ) )
- {
- if ( null != callerAnswer )
- {
- throw new Exception( "You have already submitted your answer!\n" );
- }
- callerAnswer = answer;
- return null != daredAnswer;
- }
- else if ( contestant.equals( daredNick ) )
- {
- if ( null != daredAnswer )
- {
- throw new Exception( "You have already submitted your answer!\n" );
- }
- daredAnswer = answer;
- return null != callerAnswer;
- }
- throw new Exception( "You are not playing in this RPS game!\n" );
- }
-
- private String getWinner()
- {
- if ( callerAnswer.equals( daredAnswer ) )
- {
- return null;
- }
-
- if ( firstBeatsSecond( callerAnswer, daredAnswer ) )
- {
- return callerNick;
- }
- return daredNick;
- }
-
- private String getLoser()
- {
- String winner = getWinner();
- if ( null == winner )
- {
- return null;
- }
- if ( callerNick.equals( winner ) )
- {
- return daredNick;
- }
- return callerNick;
- }
-
- private static boolean firstBeatsSecond( String first, String second )
- {
- if ( first.equals( "rock" ) )
- {
- return "scissors".equals( second );
- }
- else if ( first.equals( "paper" ) )
- {
- return "rock".equals( second );
- }
- else
- {
- return "paper".equals( second );
- }
- }
- }
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher rpsMatcher = rpsPattern.matcher( text );
- Matcher answerMatcher = answerPattern.matcher( text );
- Matcher rulesMatcher = rulesPattern.matcher( text );
- Matcher cancelMatcher = cancelPattern.matcher( text );
- if ( event instanceof MessageEvent && rpsMatcher.matches() && correctChannel( (MessageEvent) event ) )
- {
- try
- {
- startRPSGame( event, rpsMatcher.group( 1 ), rpsMatcher.group( 2 ) );
- }
- catch ( Exception e )
- {
- event.respondWith( e.getMessage() );
- }
- }
- else if ( answerMatcher.matches() && correctSolutionChannel( event ) )
- {
- String answer = answerMatcher.group( 1 );
- checkAnswer( event, answer );
- }
- else if ( rulesMatcher.matches() )
- {
- event.respondPrivateMessage( getRules( event ) );
- }
- else if ( cancelMatcher.matches() )
- {
- cancelGame( event );
- }
- }
-
- private boolean correctChannel( MessageEvent event )
- {
- String currChannel = event.getChannel().getName();
- if ( RPS_CHANNEL.equals( currChannel ) )
- {
- return true;
- }
- else
- {
- event.respondWith( "To play Rock Paper Scissors join : " + RPS_CHANNEL );
- return false;
- }
- }
-
- private boolean isPrivate( GenericMessageEvent event )
- {
- return event instanceof PrivateMessageEvent;
- }
-
- private boolean inGameChannel( GenericMessageEvent event )
- {
- return event instanceof MessageEvent && ( (MessageEvent) event ).getChannel().getName().equals( RPS_CHANNEL );
- }
-
- private boolean correctSolutionChannel( GenericMessageEvent event )
- {
- if ( ( isPrivate( event ) && inGameChannel( event ) ) )
- {
- return true;
- }
- else if ( ( (MessageEvent) event ).getChannel().getName().equals( RPS_CHANNEL ) )
- {
- event.respondWith( getSubmissionRuleString( event ) );
- return false;
- }
- else
- {
- event.respondWith( "To play Rock Paper Scissors join : " + RPS_CHANNEL );
- return false;
- }
- }
-
- private synchronized void cancelGame( GenericMessageEvent event )
- {
- if ( null != currGame )
- {
- String caller = BaseNick.toBaseNick( event.getUser().getNick() );
- if ( caller.equals( currGame.getCaller() ) || caller.equals( currGame.getDared() ) )
- {
- currGame = null;
- event.respondWith( "RPS game cancelled" );
- }
- else
- {
- event.respondWith( "Only " + currGame.getCaller() + " or " + currGame.getDared()
- + " can cancel this game." );
- }
- }
- else
- {
- event.respondWith( "No active game. Start a new one with !rps dared" );
- }
- }
-
- private synchronized void startRPSGame( GenericMessageEvent event, String dared, String daredThing )
- {
- if ( null == currGame )
- {
- String caller = BaseNick.toBaseNick( event.getUser().getNick() );
- if ( caller.equals( dared ) )
- {
- event.respondWith( "You cannot challenge yourself to a Rock Paper Scissors game!" );
- return;
- }
- if ( !daredThing.trim().isEmpty() )
- {
- currGame = new RPSGame( caller, dared, daredThing.trim(), event );
- }
- else
- {
- currGame = new RPSGame( caller, dared, null, event );
- }
- event.respondWith( currGame.getRPSIntro() + getSubmissionRuleString( event ) );
- }
- else
- {
- event.respondWith( currGame.alreadyPlaying() );
- }
- }
-
- private synchronized void checkAnswer( GenericMessageEvent event, String submission )
- {
- String contestant = BaseNick.toBaseNick( event.getUser().getNick() );
- if ( currGame != null )
- {
- try
- {
- boolean endGame = currGame.setSubmission( contestant, submission );
- event.respondWith( String.format( "Your submission of %s has been recieved!", submission ) );
- if ( endGame )
- {
- currGame.getEvent().respondWith( currGame.getRPSOutro() );
- currGame = null;
- }
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- event.respondWith( e.getMessage() );
- }
- }
- else
- {
- event.respondWith( "No active game. Start a new one with !rps dared [bet]" );
- }
-
- }
-
- private String getRules( GenericMessageEvent event )
- {
- return RED + "RPS RULES: \n" + RESET + "1) Dare someone to play rock paper scissors with you! \n" + RESET
- + "2) Rock beats scissors, paper beats rocks, and scissors beat paper \n" + "3) Use /msg "
- + event.getBot().getNick() + " !(rock|paper|scissors) to set your action. Cannot be undone.";
- }
-
- private String getSubmissionRuleString( GenericMessageEvent event )
- {
- return RED + "Use \" /msg " + RESET + event.getBot().getNick() + RED + " !(rock|paper|scissors) \" to submit."
- + RESET;
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Summon.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Summon.java
deleted file mode 100644
index 39e88527..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Summon.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.util.BaseNick;
-import com.oldterns.vilebot.util.LimitCommand;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-public class Summon
- extends ListenerAdapter
-{
- private static final Pattern nounPattern = Pattern.compile( "\\S+" );
-
- private static final Pattern summonPattern = Pattern.compile( "^!summon (" + nounPattern + ")\\s*$" );
-
- public static LimitCommand limitCommand = new LimitCommand();
-
- private static final String RESTRICTED_CHANNEL = Vilebot.getConfig().get( "ircChannel1" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
-
- if ( event instanceof MessageEvent
- && ( (MessageEvent) event ).getChannel().getName().equals( RESTRICTED_CHANNEL ) )
- {
- String text = event.getMessage();
-
- Matcher summonMatcher = summonPattern.matcher( text );
-
- if ( summonMatcher.matches() )
- {
- String toSummon = summonMatcher.group( 1 ).trim();
- if ( ( (MessageEvent) event ).getChannel().getUsers().stream().filter( u -> u.getNick().equals( toSummon ) ).findAny().isPresent() )
- {
- event.respondWith( toSummon + " is already in the channel." );
- }
- else
- {
- String isLimit = limitCommand.addUse( event.getUser().getNick() );
- if ( isLimit.isEmpty() )
- summonNick( event, toSummon );
- else
- event.respondWith( isLimit );
- }
- }
- }
- }
-
- private void summonNick( GenericMessageEvent event, String nick )
- {
- event.getBot().sendIRC().invite( nick, RESTRICTED_CHANNEL );
- event.getBot().sendIRC().message( nick, "You are summoned by "
- + BaseNick.toBaseNick( event.getUser().getNick() ) + " to join " + RESTRICTED_CHANNEL );
- event.respond( nick + " was invited." );
-
- }
-
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Trivia.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Trivia.java
deleted file mode 100644
index d7f66538..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Trivia.java
+++ /dev/null
@@ -1,317 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import com.oldterns.vilebot.db.KarmaDB;
-import com.oldterns.vilebot.util.BaseNick;
-import info.debatty.java.stringsimilarity.NormalizedLevenshtein;
-import org.jsoup.Jsoup;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.events.MessageEvent;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import twitter4j.JSONArray;
-import twitter4j.JSONObject;
-
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Created by eunderhi on 25/07/16. Simple Jeopardy implementation.
- */
-
-public class Trivia
- extends ListenerAdapter
-{
-
- private static final Pattern questionPattern = Pattern.compile( "^!jeopardy" );
-
- private static final Pattern answerPattern = Pattern.compile( "^!(whatis|whois) (.*)" );
-
- private static TriviaGame currentGame = null;
-
- private static final String JEOPARDY_CHANNEL = Vilebot.getConfig().get( "jeopardyChannel" );
-
- private static final long TIMEOUT = 30000L;
-
- private static final String RED = "\u000304";
-
- private static final String RESET = "\u000f";
-
- private static final String BLUE = "\u000302";
-
- private static final String GREEN = "\u000303";
-
- private static ExecutorService timer = Executors.newScheduledThreadPool( 1 );
-
- private static final Integer RECURSION_LIMIT = 10;
-
- private static Integer CURRENT_RECURSION_DEPTH = 0;
-
- private static final String WELCOME_STRING = "Welcome to Bot Jeopardy!";
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher questionMatcher = questionPattern.matcher( text );
- Matcher answerMatcher = answerPattern.matcher( text );
-
- try
- {
- if ( questionMatcher.matches() && shouldStartGame( event ) )
- {
- startGame( event );
- }
- else if ( answerMatcher.matches() && shouldStartGame( event ) )
- {
- String answer = answerMatcher.group( 2 );
- finishGame( event, answer );
- }
- }
- catch ( Exception e )
- {
- event.respondWith( "I don't feel like playing." );
- e.printStackTrace();
- }
- }
-
- private boolean shouldStartGame( GenericMessageEvent event )
- {
- if ( event instanceof MessageEvent
- && ( (MessageEvent) event ).getChannel().getName().equals( JEOPARDY_CHANNEL ) )
- {
- return true;
- }
- event.respondWith( "To play jeopardy join: " + JEOPARDY_CHANNEL );
- return false;
- }
-
- private synchronized void startGame( GenericMessageEvent event )
- throws Exception
- {
- if ( currentGame != null )
- {
- for ( String msg : currentGame.getAlreadyPlayingString() )
- {
- event.respondWith( msg );
- }
- }
- else
- {
- currentGame = new TriviaGame();
- event.respondWith( WELCOME_STRING );
- for ( String msg : currentGame.getIntroString() )
- {
- event.respondWith( msg );
- }
- startTimer( event );
- }
- }
-
- private void startTimer( final GenericMessageEvent event )
- {
- timer.submit( () -> {
- try
- {
- Thread.sleep( TIMEOUT );
- timeoutTimer( event );
- }
- catch ( InterruptedException e )
- {
- e.printStackTrace();
- }
- } );
- }
-
- private void timeoutTimer( GenericMessageEvent event )
- {
- for ( String msg : currentGame.getTimeoutString() )
- {
- event.respondWith( msg );
- }
- currentGame = null;
- }
-
- private void stopTimer()
- {
- timer.shutdownNow();
- timer = Executors.newFixedThreadPool( 1 );
- }
-
- private synchronized void finishGame( GenericMessageEvent event, String answer )
- {
- String answerer = BaseNick.toBaseNick( event.getUser().getNick() );
- if ( currentGame != null )
- {
- if ( currentGame.isCorrect( answer ) )
- {
- stopTimer();
- event.respondWith( String.format( "Congrats %s, you win %d karma!", answerer,
- currentGame.getStakes() ) );
- KarmaDB.modNounKarma( answerer, currentGame.getStakes() );
- currentGame = null;
- }
- else
- {
- event.respondWith( String.format( "Sorry %s! That is incorrect, you lose %d karma.", answerer,
- currentGame.getStakes() ) );
- KarmaDB.modNounKarma( answerer, -1 * currentGame.getStakes() );
- }
- }
- else
- {
- event.respondWith( "No active game. Start a new one with !jeopardy" );
- }
- }
-
- private static class TriviaGame
- {
-
- private int stakes;
-
- private String question;
-
- private String answer;
-
- private String category;
-
- private static final String API_URL = "http://jservice.io/api/random";
-
- TriviaGame()
- throws Exception
- {
- JSONObject triviaJSON = getQuestionJSON();
- if ( triviaJSON != null )
- {
- question = triviaJSON.getString( "question" );
- category = triviaJSON.getJSONObject( "category" ).getString( "title" );
- answer = Jsoup.parse( triviaJSON.getString( "answer" ) ).text();
- stakes = getStakes( triviaJSON );
- }
- else
- {
- question = "Could not find a question";
- category = "Could not find a question";
- answer = "Could not find a question";
- stakes = 0;
- }
- }
-
- private int getStakes( JSONObject trivia )
- throws Exception
- {
- if ( trivia.has( "value" ) && !trivia.isNull( "value" ) )
- {
- return trivia.getInt( "value" ) / 100;
- }
- return 5;
- }
-
- String getQuestion()
- {
- return question;
- }
-
- String getAnswer()
- {
- return answer;
- }
-
- int getStakes()
- {
- return stakes;
- }
-
- private boolean isCorrect( String answer )
- {
- String formattedUserAnswer = formatAnswer( answer );
- String formattedActualAnswer = formatAnswer( this.answer );
- double distance = new NormalizedLevenshtein().distance( formattedActualAnswer, formattedUserAnswer );
- return distance < 0.5;
- }
-
- private String formatAnswer( String answer )
- {
- return answer.toLowerCase().replaceAll( "^the ",
- "" ).replaceAll( "^a ",
- "" ).replaceAll( "^an ",
- "" ).replaceAll( "\\(.*\\)",
- "" ).replaceAll( "/.*",
- "" ).replaceAll( "&",
- "and" ).replaceAll( "[^A-Za-z\\d]",
- "" );
- }
-
- private List getQuestionBlurb()
- {
- List questionBlurb = new ArrayList<>();
- questionBlurb.add( "Your category is: " + RED + category + RESET );
- questionBlurb.add( "For " + GREEN + stakes + RESET + " karma:" );
- questionBlurb.add( BLUE + question + RESET );
- return questionBlurb;
- }
-
- List getIntroString()
- {
- List introString = new ArrayList<>( getQuestionBlurb() );
- introString.add( "30 seconds on the clock." );
- return introString;
- }
-
- List getAlreadyPlayingString()
- {
- List alreadyPlayingString = new ArrayList<>();
- alreadyPlayingString.add( "A game is already in session!" );
- alreadyPlayingString.addAll( getQuestionBlurb() );
- return alreadyPlayingString;
- }
-
- List getTimeoutString()
- {
- List timeoutString = new ArrayList<>();
- timeoutString.add( "Your 30 seconds is up! The answer we were looking for was:" );
- timeoutString.add( BLUE + answer + RESET );
- return timeoutString;
- }
-
- private String getQuestionContent()
- throws Exception
- {
- String content;
- URLConnection connection;
- connection = new URL( API_URL ).openConnection();
- connection.addRequestProperty( "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" );
- Scanner scanner = new Scanner( connection.getInputStream() );
- scanner.useDelimiter( "\\Z" );
- content = scanner.next();
- return content;
- }
-
- private JSONObject getQuestionJSON()
- throws Exception
- {
- String triviaContent = getQuestionContent();
- JSONObject triviaJSON = new JSONArray( triviaContent ).getJSONObject( 0 );
- String question = triviaJSON.getString( "question" ).trim();
- boolean invalidFlag = !( triviaJSON.getString( "invalid_count" ).equals( "null" ) );
- if ( ( question.equals( "" ) || question.contains( "seen here" ) || invalidFlag )
- && ( CURRENT_RECURSION_DEPTH < RECURSION_LIMIT ) )
- {
- CURRENT_RECURSION_DEPTH += 1;
- return getQuestionJSON();
- }
- else if ( CURRENT_RECURSION_DEPTH >= RECURSION_LIMIT )
- {
- return null;
- }
- return triviaJSON;
- }
- }
-
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/TwitterCorrection.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/TwitterCorrection.java
deleted file mode 100644
index 8fa527b2..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/TwitterCorrection.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright (C) 2013-2014 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class TwitterCorrection
- extends ListenerAdapter
-{
- private static final Pattern twitterSyntaxUsePattern = Pattern.compile( "(?:^|\\s+)[@](\\S+)(?:\\s|:|)" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
- Matcher matcher = twitterSyntaxUsePattern.matcher( text );
-
- if ( matcher.find() )
- {
- String word = matcher.group( 1 );
-
- String sb = "You seem to be using twitter addressing syntax. On IRC you would say this instead: "
- + word.replaceAll( "[^A-Za-z0-9]$", "" ) + ": message";
- event.respond( sb );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UrlTitleAnnouncer.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UrlTitleAnnouncer.java
deleted file mode 100644
index 20b9c2eb..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UrlTitleAnnouncer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.io.IOException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Will find the HTML title of HTTP(S) pages from certain domains. Generally it's only worth adding trustworthy domains
- * that don't include titles in the URL for SEO.
- */
-
-public class UrlTitleAnnouncer
- extends ListenerAdapter
-{
-
- private static final Pattern urlPattern =
- Pattern.compile( "((?:http|https)://(?:www.|)(?:(?:abstrusegoose|xkcd)\\.com|youtube\\.(?:com|ca)|youtu\\.be)[^ ]*)" );
-
- private static final Pattern titlePattern = Pattern.compile( "(.*) " );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- Matcher urlMatcher = urlPattern.matcher( event.getMessage() );
-
- if ( urlMatcher.find() )
- {
- String title = scrapeURLHTMLTitle( urlMatcher.group( 1 ) );
- event.respondWith( "'" + title + "'" );
- }
- }
-
- /**
- * Accesses the source of a HTML page and looks for a title element
- *
- * @param url http URI String
- * @return String of text between the first tag group on the page, empty if error.
- */
- private String scrapeURLHTMLTitle( String url )
- {
- String title = "";
-
- try
- {
- Document doc = Jsoup.connect( url ).get();
- title = doc.title();
- }
- catch ( IOException x )
- {
- System.err.format( "scrapeURLHTMLTitle BufferedReader error: %s%n", x );
- }
-
- return title;
- }
-
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UrlTweetAnnouncer.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UrlTweetAnnouncer.java
deleted file mode 100644
index c2a10c50..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UrlTweetAnnouncer.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * Copyright (C) 2014 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.Vilebot;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import twitter4j.Status;
-import twitter4j.Twitter;
-import twitter4j.TwitterException;
-import twitter4j.TwitterFactory;
-import twitter4j.User;
-import twitter4j.conf.ConfigurationBuilder;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Will grab the text from a tweet given the static tweet URL. To enable, add the following entries to vilebot.conf:
- * consumerKey consumerSecret accessToken accessTokenSecret Where all of the above are created at
- * https://apps.twitter.com
- */
-
-public class UrlTweetAnnouncer
- extends ListenerAdapter
-{
-
- private static final Pattern urlPattern =
- Pattern.compile( "((?:http|https)://(?:www.|)(?:(?:twitter)\\.com)[^ ]*)" );
-
- private static final Pattern titlePattern = Pattern.compile( "(.*) " );
-
- private final Map cfg = Vilebot.getConfig();
-
- private final String consumerKey = cfg.get( "consumerKey" ); // may be known as 'API key'
-
- private final String consumerSecret = cfg.get( "consumerSecret" ); // may be known as 'API secret'
-
- private final String accessToken = cfg.get( "accessToken" ); // may be known as 'Access token'
-
- private final String accessTokenSecret = cfg.get( "accessTokenSecret" ); // may be known as 'Access token secret'
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- Matcher urlMatcher = urlPattern.matcher( event.getMessage() );
-
- if ( urlMatcher.find() )
- {
- if ( consumerKey == null || consumerSecret == null || accessToken == null || accessTokenSecret == null )
- {
- event.respondWith( "Sorry, I can't read that tweet because my maintainer is a moron. And I wouldn't want to read it, anyway." );
- return;
- }
-
- String title = scrapeURLHTMLTitle( event, urlMatcher.group( 1 ) );
- event.respondWith( "' " + title + " '" );
- }
- }
-
- /**
- * Accesses the source of a HTML page and looks for a title element
- *
- * @param url http tweet String
- * @return String of text which represents the tweet. Empty if error.
- */
- private String scrapeURLHTMLTitle( GenericMessageEvent event, String url )
- {
- String text = "";
-
- try
- {
- new URL( url );
- }
- catch ( MalformedURLException x )
- {
- // System.err.format("scrapeURLHTMLTitle new URL error: %s%n", x);
- return text;
- }
-
- // split the url into pieces, change the request based on what we have
- String[] parts = url.split( "/" );
- int userPosition = 0;
- long tweetID = 0;
- for ( int i = 0; i < parts.length; i++ )
- {
- if ( parts[i].equals( "twitter.com" ) )
- userPosition = i + 1;
- if ( parts[i].equals( "status" ) || parts[i].equals( "statuses" ) )
- tweetID = Long.valueOf( parts[i + 1] );
- }
- if ( userPosition == 0 )
- return text;
- else
- {
- try
- {
- ConfigurationBuilder cb = new ConfigurationBuilder();
- cb.setDebugEnabled( true ).setOAuthConsumerKey( consumerKey ).setOAuthConsumerSecret( consumerSecret ).setOAuthAccessToken( accessToken ).setOAuthAccessTokenSecret( accessTokenSecret );
- TwitterFactory tf = new TwitterFactory( cb.build() );
- Twitter twitter = tf.getInstance();
- if ( tweetID != 0 ) // tweet of the twitter.com/USERID/status/TWEETID variety
- {
- Status status = twitter.showStatus( tweetID );
- return ( status.getUser().getName() + ": " + status.getText() );
- }
- else // just the user is given, ie, twitter.com/USERID
- {
- User user = twitter.showUser( parts[userPosition].toString() );
- if ( !user.getDescription().isEmpty() ) // the user has a description
- return ( "Name: " + user.getName() + " | " + user.getDescription() + "\'\nLast Tweet: \'"
- + user.getStatus().getText() );
- else // the user doesn't have a description, don't print it
- return ( "Name: " + user.getName() + "\'\nLast Tweet: \'" + user.getStatus().getText() );
-
- }
- }
- catch ( TwitterException x )
- {
- return event.getBot().getNick()
- + ": Until my maintainer fixes the API Key, this is the only tweet you're gonna see. U mad, bro?";
- }
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UserPing.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UserPing.java
deleted file mode 100644
index b34b2c08..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/UserPing.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (C) 2013 Oldterns
- *
- * This file may be modified and distributed under the terms
- * of the MIT license. See the LICENSE file for details.
- */
-package com.oldterns.vilebot.handlers.user;
-
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-public class UserPing
- extends ListenerAdapter
-{
- /**
- * Reply to user !ping command with "nickname, pong"
- */
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
-
- if ( text.startsWith( "!ping" ) )
- {
- String user = event.getUser().getNick();
- event.respondWith( user + ", pong" );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Userlists.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Userlists.java
deleted file mode 100644
index 3bf80061..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Userlists.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.db.UserlistDB;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Userlists
- extends ListenerAdapter
-{
- private static final Pattern enumeratePattern = Pattern.compile( "!lists" );
-
- private static final Pattern queryPattern = Pattern.compile( "!list (\\S+)" );
-
- private static final Pattern nickBlobPattern = Pattern.compile( "(?:(\\S+?)(?:, +| +|$))" );
-
- private static final Pattern addRemovePattern =
- Pattern.compile( "!list(add|rem) (\\S+) (" + nickBlobPattern + "+)" );
-
- private Pattern expandPattern = Pattern.compile( "(\\S+): (.*)" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
-
- Matcher enumerateMatcher = enumeratePattern.matcher( text );
- Matcher queryMatcher = queryPattern.matcher( text );
- Matcher addRemoveMatcher = addRemovePattern.matcher( text );
- Matcher expandMatcher = expandPattern.matcher( text );
-
- if ( enumerateMatcher.matches() )
- listsEnumerate( event );
- if ( queryMatcher.matches() )
- listQuery( event, queryMatcher );
- if ( addRemoveMatcher.matches() )
- listAddRemove( event, addRemoveMatcher );
- if ( expandMatcher.matches() )
- listExpansion( event, expandMatcher );
- }
-
- private void listsEnumerate( GenericMessageEvent event )
- {
- Set lists = UserlistDB.getLists();
-
- if ( lists != null && lists.size() > 0 )
- {
- StringBuilder sb = new StringBuilder();
- sb.append( "Available lists: " );
- for ( String list : lists )
- {
- sb.append( list );
- sb.append( ", " );
- }
- sb.delete( sb.length() - 2, sb.length() );
- event.respondWith( sb.toString() );
- }
- else
- {
- event.respondWith( "There are no lists." );
- }
- }
-
- private void listQuery( GenericMessageEvent event, Matcher matcher )
- {
- String listName = matcher.group( 1 );
-
- Set users = UserlistDB.getUsersIn( listName );
- if ( users != null && users.size() > 0 )
- {
- StringBuilder sb = new StringBuilder();
- sb.append( "The list " );
- sb.append( listName );
- sb.append( " contains: " );
-
- for ( String user : users )
- {
- sb.append( user );
- sb.append( ", " );
- }
- sb.delete( sb.length() - 2, sb.length() );
-
- event.respondPrivateMessage( sb.toString() );
- }
- else
- {
- event.respondPrivateMessage( "The list " + listName + " does not exist or is empty." );
- }
- }
-
- private void listAddRemove( GenericMessageEvent event, Matcher matcher )
- {
- String mode = matcher.group( 1 );
- String listName = matcher.group( 2 );
- String nickBlob = matcher.group( 3 );
- if ( nickBlob == null )
- {
- nickBlob = matcher.group( 4 );
- }
-
- List nicks = new LinkedList<>();
- Matcher nickMatcher = nickBlobPattern.matcher( nickBlob );
- while ( nickMatcher.find() )
- {
- nicks.add( nickMatcher.group( 1 ) );
- }
-
- StringBuilder sb = new StringBuilder();
-
- if ( "add".equals( mode ) )
- {
- UserlistDB.addUsersTo( listName, nicks );
- sb.append( "Added the following names to list " );
- }
- else if ( "rem".equals( mode ) )
- {
- UserlistDB.removeUsersFrom( listName, nicks );
- sb.append( "Removed the following names from list " );
- }
-
- sb.append( listName );
- sb.append( ": " );
-
- for ( String nick : nicks )
- {
- sb.append( nick );
- sb.append( ", " );
- }
- sb.delete( sb.length() - 2, sb.length() );
-
- event.respondWith( sb.toString() );
- }
-
- private void listExpansion( GenericMessageEvent event, Matcher matcher )
- {
- String sender = event.getUser().getNick();
- String listName = matcher.group( 1 );
- String msg = matcher.group( 2 );
-
- Set users = UserlistDB.getUsersIn( listName );
- if ( users != null && users.size() > 0 )
- {
- StringBuilder sb = new StringBuilder();
- for ( String user : users )
- {
- if ( !user.equals( sender ) )
- {
- sb.append( user );
- sb.append( ", " );
- }
- }
- sb.delete( sb.length() - 2, sb.length() );
- sb.append( ": " );
- sb.append( msg );
-
- event.respondWith( sb.toString() );
- }
- }
-}
diff --git a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Weather.java b/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Weather.java
deleted file mode 100644
index 4dd79be1..00000000
--- a/vilebot/src/main/java/com/oldterns/vilebot/handlers/user/Weather.java
+++ /dev/null
@@ -1,323 +0,0 @@
-package com.oldterns.vilebot.handlers.user;
-
-import com.oldterns.vilebot.util.Ignore;
-import com.sun.syndication.feed.synd.SyndEntry;
-import com.sun.syndication.feed.synd.SyndFeed;
-import com.sun.syndication.io.FeedException;
-import com.sun.syndication.io.SyndFeedInput;
-import com.sun.syndication.io.XmlReader;
-import org.pircbotx.hooks.ListenerAdapter;
-import org.pircbotx.hooks.types.GenericMessageEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Weather
- extends ListenerAdapter
-{
-
- private static final Logger logger = LoggerFactory.getLogger( Weather.class );
-
- private static final String LESS_NICK = "owilliams";
- static
- {
- Ignore.addOnJoin( LESS_NICK );
- }
-
- private static final String defaultLocation = "ytz";
-
- private static final HashMap weatherFeedsByIataCode = new LinkedHashMap<>();
- static
- {
- try
- {
- weatherFeedsByIataCode.put( "yxu", new URL( "https://weather.gc.ca/rss/city/on-137_e.xml" ) );
- weatherFeedsByIataCode.put( "yyz", new URL( "https://weather.gc.ca/rss/city/on-143_e.xml" ) );
- weatherFeedsByIataCode.put( "ygk", new URL( "https://weather.gc.ca/rss/city/on-69_e.xml" ) );
- weatherFeedsByIataCode.put( "yeg", new URL( "https://weather.gc.ca/rss/city/ab-50_e.xml" ) );
- weatherFeedsByIataCode.put( "yyc", new URL( "https://weather.gc.ca/rss/city/ab-52_e.xml" ) );
- weatherFeedsByIataCode.put( "yul", new URL( "https://weather.gc.ca/rss/city/qc-147_e.xml" ) );
- weatherFeedsByIataCode.put( "yvr", new URL( "https://weather.gc.ca/rss/city/bc-74_e.xml" ) );
- weatherFeedsByIataCode.put( "yhm", new URL( "https://weather.gc.ca/rss/city/on-77_e.xml" ) );
- weatherFeedsByIataCode.put( "ytz", new URL( "https://weather.gc.ca/rss/city/on-128_e.xml" ) );
- weatherFeedsByIataCode.put( "ykz", new URL( "https://weather.gc.ca/rss/city/on-85_e.xml" ) );
- weatherFeedsByIataCode.put( "ykf", new URL( "https://weather.gc.ca/rss/city/on-82_e.xml" ) );
- weatherFeedsByIataCode.put( "yhz", new URL( "https://weather.gc.ca/rss/city/ns-19_e.xml" ) );
- weatherFeedsByIataCode.put( "yfc", new URL( "https://weather.gc.ca/rss/city/nb-29_e.xml" ) );
- weatherFeedsByIataCode.put( "yyt", new URL( "https://weather.gc.ca/rss/city/nl-24_e.xml" ) );
- weatherFeedsByIataCode.put( "yyg", new URL( "https://weather.gc.ca/rss/city/pe-5_e.xml" ) );
- weatherFeedsByIataCode.put( "yqt", new URL( "https://weather.gc.ca/rss/city/on-100_e.xml" ) );
- weatherFeedsByIataCode.put( "ywg", new URL( "https://weather.gc.ca/rss/city/mb-38_e.xml" ) );
- weatherFeedsByIataCode.put( "yqr", new URL( "https://weather.gc.ca/rss/city/sk-32_e.xml" ) );
- weatherFeedsByIataCode.put( "yxe", new URL( "https://weather.gc.ca/rss/city/sk-40_e.xml" ) );
- weatherFeedsByIataCode.put( "ymm", new URL( "https://weather.gc.ca/rss/city/ab-20_e.xml" ) );
- weatherFeedsByIataCode.put( "yxs", new URL( "https://weather.gc.ca/rss/city/bc-79_e.xml" ) );
- weatherFeedsByIataCode.put( "yyj", new URL( "https://weather.gc.ca/rss/city/bc-85_e.xml" ) );
- weatherFeedsByIataCode.put( "ylw", new URL( "https://weather.gc.ca/rss/city/bc-48_e.xml" ) );
- weatherFeedsByIataCode.put( "yka", new URL( "https://weather.gc.ca/rss/city/bc-45_e.xml" ) );
- weatherFeedsByIataCode.put( "yow", new URL( "https://weather.gc.ca/rss/city/on-118_e.xml" ) );
- weatherFeedsByIataCode.put( "yxy", new URL( "https://weather.gc.ca/rss/city/yt-16_e.xml" ) );
- weatherFeedsByIataCode.put( "yzf", new URL( "https://weather.gc.ca/rss/city/nt-24_e.xml" ) );
- weatherFeedsByIataCode.put( "yfb", new URL( "https://weather.gc.ca/rss/city/nu-21_e.xml" ) );
- weatherFeedsByIataCode.put( "ylt", new URL( "https://weather.gc.ca/rss/city/nu-22_e.xml" ) );
- }
- catch ( MalformedURLException e )
- {
- logger.error( "Error loading weather URLs" );
- throw new RuntimeException( e );
- }
- }
-
- private static HashMap weatherDataByIataCode = new HashMap<>();
-
- private static final long weatherDataCacheTime = 1000 * 60 * 30;
-
- private static final Pattern weatherPattern = Pattern.compile( "!(less|more|)weather(?: ([a-zA-Z]{3})|)" );
-
- private static final Pattern forecastPattern = Pattern.compile( "!forecast(?: ([a-zA-Z]{3})|)" );
-
- @Override
- public void onGenericMessage( final GenericMessageEvent event )
- {
- String text = event.getMessage();
-
- Matcher forecastMatcher = forecastPattern.matcher( text );
- Matcher weatherMatcher = weatherPattern.matcher( text );
-
- if ( forecastMatcher.matches() )
- forecastWeather( event, forecastMatcher );
- if ( weatherMatcher.matches() )
- currentWeather( event, weatherMatcher );
- }
-
- private void forecastWeather( GenericMessageEvent event, Matcher matcher )
- {
- String locationCode = matcher.group( 1 ); // The IATA code
- if ( locationCode == null )
- {
- locationCode = defaultLocation;
- }
- locationCode = locationCode.toLowerCase();
-
- if ( weatherFeedsByIataCode.containsKey( locationCode ) )
- {
- WeatherData weather = getWeatherFor( locationCode );
- if ( weather == null )
- {
- event.respondWith( "Error reading weather feed for " + locationCode );
- }
- else
- {
- StringBuilder sb = new StringBuilder();
- for ( Entry forecastDay : weather.getForecast().entrySet() )
- {
- sb.append( "[" );
- sb.append( forecastDay.getKey() );
- sb.append( ": " );
- sb.append( forecastDay.getValue() );
- sb.append( "] " );
- }
- event.respondWith( sb.toString() );
- }
- }
- }
-
- private void currentWeather( GenericMessageEvent event, Matcher matcher )
- {
- String modifier = matcher.group( 1 ); // Either less or more or null
- String locationCode = matcher.group( 2 ); // The IATA code
- if ( locationCode == null )
- {
- locationCode = defaultLocation;
- }
- locationCode = locationCode.toLowerCase();
-
- if ( weatherFeedsByIataCode.containsKey( locationCode ) )
- {
- WeatherData weather = getWeatherFor( locationCode );
- if ( weather == null )
- {
- event.respondWith( "Error reading weather feed for " + locationCode );
- }
- else
- {
- if ( !"less".equals( modifier ) )
- {
- for ( String alert : weather.getAlerts() )
- {
- event.respondWith( alert );
- }
- }
-
- LinkedHashMap currentConditions = weather.getCurrentConditions();
- if ( "".equals( modifier ) )
- {
-
- String sb = currentConditions.get( "Condition" ) + ", Temperature: "
- + currentConditions.get( "Temperature" ) + ", Humidity: " + currentConditions.get( "Humidity" )
- + " - " + currentConditions.get( "Observed at" );
- event.respondWith( sb );
- }
- else if ( "less".equals( modifier ) )
- {
- // bot.sendPrivmsgAs( LESS_NICK, event.getChannel(),
- // "IT'S " + currentConditions.get( "Condition" ).toUpperCase() );
- event.respondWith( "IT'S " + currentConditions.get( "Condition" ).toUpperCase() );
- }
- else if ( "more".equals( modifier ) )
- {
- StringBuilder sb = new StringBuilder();
- for ( Entry condition : currentConditions.entrySet() )
- {
- sb.append( "[" );
- sb.append( condition.getKey() );
- sb.append( ": " );
- sb.append( condition.getValue() );
- sb.append( "] " );
- }
- event.respondWith( sb.toString() );
- }
- }
- }
- else
- {
- event.respondWith( "No weather feed available for " + locationCode );
- }
- }
-
- private WeatherData getWeatherFor( String locationCode )
- {
- if ( weatherDataByIataCode.containsKey( locationCode ) )
- {
- WeatherData weather = weatherDataByIataCode.get( locationCode );
-
- long timeDiff = new Date().getTime() - weather.getCreationDate().getTime();
- // Cache for at most half an hour
- if ( timeDiff < weatherDataCacheTime )
- {
- return weather;
- }
- }
-
- URL feedSource = weatherFeedsByIataCode.get( locationCode );
- WeatherData weather = null;
- try
- {
- weather = new WeatherData( feedSource );
- weatherDataByIataCode.put( locationCode, weather );
- }
- catch ( IllegalArgumentException | IOException | FeedException e )
- {
- logger.error( e.getMessage() );
- logger.error( "Error opening RSS feed" );
- }
- return weather;
- }
-
- /**
- * Stores weather data from http://weather.gc.ca/rss/city/* rss feeds flexibly.
- */
- private class WeatherData
- {
- private final Pattern alertIDPattern = Pattern.compile( ".*_w[0-9]+:[0-9]{14}$" );
-
- private final Pattern currentConditionsIDPattern = Pattern.compile( ".*_cc:[0-9]{14}$" );
-
- private final Pattern forecastIDPattern = Pattern.compile( ".*_fc[0-9]:[0-9]{14}$" );
-
- private final LinkedList alerts;
-
- private final LinkedHashMap currentConditions;
-
- private final LinkedHashMap forecast;
-
- private final Date creationTime;
-
- WeatherData( URL feedSource )
- throws IOException, IllegalArgumentException, FeedException
- {
- alerts = new LinkedList<>();
- forecast = new LinkedHashMap<>();
- currentConditions = new LinkedHashMap<>();
- creationTime = new Date();
-
- try ( XmlReader reader = new XmlReader( feedSource ) )
- {
- SyndFeedInput input = new SyndFeedInput();
- SyndFeed feed = input.build( reader );
-
- for ( Object rawEntry : feed.getEntries() )
- {
- if ( rawEntry instanceof SyndEntry )
- {
- SyndEntry entry = (SyndEntry) rawEntry;
-
- if ( alertIDPattern.matcher( entry.getUri() ).matches() )
- {
- String desc = entry.getDescription().getValue();
- if ( !desc.equals( "No watches or warnings in effect." ) )
- {
- alerts.add( desc );
- }
- }
- else if ( currentConditionsIDPattern.matcher( entry.getUri() ).matches() )
- {
- String desc = entry.getDescription().getValue();
- desc = desc.replaceAll( "<[a-zA-Z/]+>", "" );
- desc = desc.replace( "°", "°" );
- for ( String line : desc.split( "\n" ) )
- {
- int sepPos = line.indexOf( ':' );
- currentConditions.put( line.substring( 0, sepPos ),
- line.substring( sepPos + 1 ).trim() );
- }
- }
- else if ( forecastIDPattern.matcher( entry.getUri() ).matches() )
- {
- String title = entry.getTitle();
- int sepPos = title.indexOf( ':' );
- forecast.put( title.substring( 0, sepPos ), title.substring( sepPos + 1 ).trim() );
- }
- }
- }
- }
- }
-
- LinkedList getAlerts()
- {
- return alerts;
- }
-
- LinkedHashMap getCurrentConditions()
- {
- return currentConditions;
- }
-
- LinkedHashMap