diff --git a/src/main/java/com/gmail/inverseconduit/bot/Program.java b/src/main/java/com/gmail/inverseconduit/bot/Program.java index c05045d..16cbd95 100644 --- a/src/main/java/com/gmail/inverseconduit/bot/Program.java +++ b/src/main/java/com/gmail/inverseconduit/bot/Program.java @@ -47,7 +47,7 @@ public Program(ChatInterface chatInterface) throws IOException { bots.add(DefaultBot.create(chatInterface)); bots.add(new InteractionBot(chatInterface)); bots.forEach(chatInterface::subscribe); - LOGGER.log(Level.FINE, "Basic component setup complete"); + LOGGER.log(Level.INFO, "Basic component setup complete"); } /** @@ -56,7 +56,7 @@ public Program(ChatInterface chatInterface) throws IOException { * Additionally all bots that were created on startup are started. */ public void startup() { - LOGGER.log(Level.FINER, "Beginning startup process"); + LOGGER.log(Level.INFO, "Beginning startup process"); for (Integer room : config.getRooms()) { // FIXME: isn't always Stackoverflow chatInterface.joinChat(new SeChatDescriptor.DescriptorBuilder(SESite.STACK_OVERFLOW).setRoom(() -> room).build()); diff --git a/src/main/java/com/gmail/inverseconduit/chat/StackExchangeChat.java b/src/main/java/com/gmail/inverseconduit/chat/StackExchangeChat.java index 7642a5b..9ee6677 100644 --- a/src/main/java/com/gmail/inverseconduit/chat/StackExchangeChat.java +++ b/src/main/java/com/gmail/inverseconduit/chat/StackExchangeChat.java @@ -9,14 +9,25 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + import com.gargoylesoftware.htmlunit.*; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.util.NameValuePair; +import com.gargoylesoftware.htmlunit.util.UrlUtils; import com.gargoylesoftware.htmlunit.util.WebConnectionWrapper; import com.gmail.inverseconduit.SESite; import com.gmail.inverseconduit.datatype.*; import com.gmail.inverseconduit.utils.PrintUtils; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; public class StackExchangeChat implements ChatInterface { @@ -59,19 +70,75 @@ public StackExchangeChat() { */ @Override public boolean login(ProviderDescriptor descriptor, CredentialsProvider credentials) { - HtmlPage loginPage = getLoginPage(descriptor); - if (null == loginPage) { return false; } + if (descriptor.getDescription().toString().matches("(?!.*?(?:stackoverflow|meta)).+")) + loggedIn = openIdLogin(credentials.getIdentificator(), credentials.getAuthenticator()); + else { + HtmlPage loginPage = getLoginPage(descriptor); + if (null == loginPage) { return false; } - HtmlForm loginForm = extractLoginForm(credentials.getIdentificator(), credentials.getAuthenticator(), loginPage); + HtmlForm loginForm = extractLoginForm(credentials.getIdentificator(), credentials.getAuthenticator(), loginPage); - WebResponse response = submitLoginForm(loginForm); - if (null == response) { return false; } + WebResponse response = submitLoginForm(loginForm); + if (null == response) { return false; } - loggedIn = (response.getStatusCode() == 200); - logLoginMessage(descriptor.getDescription().toString(), credentials.getIdentificator(), response); + loggedIn = (response.getStatusCode() == 200); + logLoginMessage(descriptor.getDescription().toString(), credentials.getIdentificator(), response); + } return loggedIn; } + public boolean openIdLogin(String email, String password) { + try { + final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + { // Log into Stack Exchange. + WebResponse getRes = webClient.loadWebResponse(new WebRequest(UrlUtils.toUrlUnsafe("https://openid.stackexchange.com/account/login"), HttpMethod.GET)); + if (getRes == null) { + LOGGER.log(Level.SEVERE, "Could not get OpenID fkey."); + return false; + } + + String response = getRes.getContentAsString(); + LOGGER.info("Got response from fetching fkey of OpenID login:" + response); + + WebRequest post = new WebRequest(UrlUtils.toUrlUnsafe("https://openid.stackexchange.com/account/login/submit"), HttpMethod.POST); + try { + post.setRequestParameters(ImmutableList.of(new NameValuePair("email", email), new NameValuePair("password", password), + new NameValuePair("fkey", builder.parse(getRes.getContentAsStream()).getElementById("fkey").getAttribute("value")))); + } catch(SAXException die) { + LOGGER.log(Level.SEVERE, "Parsing response text as DOM has caused a parse exception.", die); + return false; + } + WebResponse postRes = webClient.loadWebResponse(post); + if (null == postRes || !Strings.isNullOrEmpty(postRes.getResponseHeaderValue("p3p"))) { + LOGGER.log(Level.SEVERE, "Could not post to submit of OpenID."); + return false; + } + } + + { // Log into Stack Exchange Chat. + WebResponse getRes = webClient.loadWebResponse(new WebRequest(UrlUtils.toUrlUnsafe("http://stackexchange.com/users/chat-login"), HttpMethod.GET)); + WebRequest post = new WebRequest(UrlUtils.toUrlUnsafe("http://chat.stackexchange.com/users/login/global"), HttpMethod.POST); + try { + Document dom = builder.parse(getRes.getContentAsStream()); + post.setRequestParameters(ImmutableList.of(new NameValuePair("authToken", dom.getElementById("authToken").getAttribute("value")), new NameValuePair("nonce", + dom.getElementById("nonce").getAttribute("value")))); + } catch(SAXException die) { + LOGGER.log(Level.SEVERE, "Parsing response text as DOM has caused a parse exception.", die); + return false; + } + post.setAdditionalHeaders(ImmutableMap.of("Origin", "http://chat.stackexchange.com", "Referer", "http://chat.stackexchange.com")); + WebResponse postRes = webClient.loadWebResponse(post); + String response = postRes.getContentAsString(); + return response.contains("Welcome"); + } + } catch(ParserConfigurationException e) { + // Ignore - virtually impossible. + } catch(IOException e) { + LOGGER.log(Level.SEVERE, "Encountered IO-Exception when trying to login: ", e); + } + return false; + } + private WebResponse submitLoginForm(HtmlForm loginForm) { WebResponse response; try { @@ -187,7 +254,7 @@ private String handleMessageOversize(final SeChatDescriptor descriptor, String m StringBuilder messageBuilder = new StringBuilder(500); for (String token : messageTokens) { if (messageBuilder.length() + token.length() < 495) { - messageBuilder.append(" " + token); + messageBuilder.append(' ').append(token); } else { LOGGER.log(Level.FINER, "Message split part: " + messageBuilder.toString()); @@ -230,7 +297,11 @@ private boolean sendMessage(String restRootUrl, String fkey, String message) { } /** - * {@inheritDoc} + * Queries the 5 latest messages for all chatrooms and enqueues them to + * the subscribed {@link ChatWorker Workers}, respecting the already handled + * timestamps as maintained internally. + * + * @see ChatInterface#queryMessages() */ @Override public void broadcast(final String message) { diff --git a/src/main/java/com/gmail/inverseconduit/scripts/ScriptRunner.java b/src/main/java/com/gmail/inverseconduit/scripts/ScriptRunner.java index 840ffb1..3314a99 100644 --- a/src/main/java/com/gmail/inverseconduit/scripts/ScriptRunner.java +++ b/src/main/java/com/gmail/inverseconduit/scripts/ScriptRunner.java @@ -55,8 +55,8 @@ public String evaluateGroovy(ChatMessage msg, String commandText) { } LOGGER.info("Result:" + result); return result == null - ? String.format(":%d [tag:groovy]: no result", msg.getMessageId()) - : String.format(":%d [tag:groovy]: %s", msg.getMessageId(), result.toString()); + ? "[tag:groovy]: no result" + : String.format("[tag:groovy]: %s", result.toString()); } public void evaluateAndCache(String commandText) {