/*
* Copyright (C) 2008-2017, Juick
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package com.juick.server.protocol;
import com.juick.Message;
import com.juick.Tag;
import com.juick.User;
import com.juick.formatters.PlainTextFormatter;
import com.juick.server.protocol.annotation.UserCommand;
import com.juick.server.util.TagUtils;
import com.juick.service.*;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import javax.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Created by oxpa on 22.03.16.
*/
public class JuickProtocol {
private String baseUri;
private ProtocolListener listener;
@Inject
UserService userService;
@Inject
TagService tagService;
@Inject
MessagesService messagesService;
@Inject
SubscriptionService subscriptionService;
@Inject
PMQueriesService pmQueriesService;
@Inject
PrivacyQueriesService privacyQueriesService;
@Inject
ShowQueriesService showQueriesService;
public JuickProtocol(String baseUri) {
this.baseUri = baseUri;
}
/**
* find command by pattern and invoke
* @param user who send command
* @param userInput given by user
* @return command result
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
*/
public String getReply(User user, String userInput) throws InvocationTargetException,
IllegalAccessException, NoSuchMethodException {
Optional cmd = MethodUtils.getMethodsListWithAnnotation(getClass(), UserCommand.class).stream()
.filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(),
m.getAnnotation(UserCommand.class).patternFlags()).matcher(userInput).matches())
.findFirst();
if (!cmd.isPresent()) {
// default command - post as new message
return postMessage(user, userInput.trim());
} else {
Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(),
cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(userInput.trim());
List groups = new ArrayList<>();
while (matcher.find()) {
for (int i = 1; i <= matcher.groupCount(); i++) {
groups.add(matcher.group(i));
}
}
return (String) getClass().getMethod(cmd.get().getName(), User.class, String[].class)
.invoke(this, user, groups.toArray(new String[groups.size()]));
}
}
public String postMessage(User user, String input) {
List tags = tagService.fromString(input, false);
String body = input.substring(TagUtils.toString(tags).length());
int mid = messagesService.createMessage(user.getUid(), body, null, tags);
subscriptionService.subscribeMessage(mid, user.getUid());
listener.messagePosted(messagesService.getMessage(mid));
return "New message posted.\n#" + mid + " " + baseUri + mid;
}
@UserCommand(pattern = "^d\\s*\\#([0-9]+)$", patternFlags = Pattern.CASE_INSENSITIVE,
help = "D #12345 - delete the message")
public String commandDel(User user, String... args) {
int mid = NumberUtils.toInt(args[0], 0);
if (messagesService.deleteMessage(user.getUid(), mid)) {
return String.format("Message %s deleted", mid);
}
return "Error";
}
@UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)")
public String commandMyFeed(User user, String... arguments) {
// number of # is the page count
int page = arguments[0].length() - 1;
List mids = messagesService.getMyFeed(user.getUid(), page, false);
List messages = messagesService.getMessages(mids);
// TODO: add instructions for empty feed
return "Your feed: \n" + String.join("\n",
messages.stream().map(PlainTextFormatter::formatPost).collect(Collectors.toList()));
}
@UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)",
help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message")
public String EditOrReply(User user, String... args) {
int mid = NumberUtils.toInt(args[1]);
int rid = NumberUtils.toInt(args[4], 0);
String txt = args[5];
List messageTags = tagService.fromString(txt, true);
if (messageTags.size() > 0) {
if (user.getUid() != messagesService.getMessageAuthor(mid).getUid()) {
return "It is not your message";
}
tagService.updateTags(mid, messageTags);
return "Tags are updated";
} else {
int newrid = messagesService.createReply(mid, rid, user.getUid(), txt, null);
listener.messagePosted(messagesService.getReply(mid, newrid));
return "Reply posted.\n#" + mid + "/" + newrid + " "
+ baseUri + mid + "#" + newrid;
}
}
@UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @user - subscribe to user's posts",
patternFlags = Pattern.CASE_INSENSITIVE)
public String commandSubscribeUser(User user, String... args) {
boolean subscribe = args[0].equalsIgnoreCase("s");
User toUser = userService.getUserByName(args[1]);
if (toUser.getUid() > 0) {
if (subscribe) {
if (subscriptionService.subscribeUser(user, toUser)) {
listener.userSubscribed(user, toUser);
return "Subscribed";
// TODO: already subscribed case
}
} else {
if (subscriptionService.unSubscribeUser(user, toUser)) {
return "Unsubscribed from @" + toUser.getName();
}
return "You was not subscribed to @" + toUser.getName();
}
}
return "Error";
}
public String getBaseUri() {
return baseUri;
}
public ProtocolListener getListener() {
return listener;
}
public void setListener(ProtocolListener listener) {
this.listener = listener;
}
}