├── .gitignore ├── .travis.yml ├── README.md ├── maven ├── maven-wrapper.jar └── maven-wrapper.properties ├── mvnw ├── mvnw.bat ├── pom.xml └── src └── main ├── java ├── .gitkeep └── be │ └── hehehe │ └── geekbot │ ├── annotations │ ├── BotCommand.java │ ├── Help.java │ ├── RandomAction.java │ ├── TimedAction.java │ ├── Trigger.java │ ├── TriggerType.java │ └── Triggers.java │ ├── bot │ ├── CommandInvoker.java │ ├── DiscordBot.java │ ├── GeekBot.java │ ├── GeekBotCDIExtension.java │ ├── MessageWriter.java │ ├── State.java │ ├── StateImpl.java │ ├── StateProducer.java │ ├── TriggerEvent.java │ └── TriggerEventImpl.java │ ├── commands │ ├── BlagueCommand.java │ ├── BuzzCommand.java │ ├── ConnerieCommand.java │ ├── DansTonChatCommand.java │ ├── EpisodesCommand.java │ ├── GoogleCommand.java │ ├── HelpCommand.java │ ├── HoroscopeCommand.java │ ├── IMDBCommand.java │ ├── MemeCommand.java │ ├── MirrorCommand.java │ ├── PileoufaceCommand.java │ ├── QuizzCommand.java │ ├── QuoteCommand.java │ ├── ReverseCommand.java │ ├── Rot13Command.java │ ├── SkanditeCommand.java │ ├── VDMCommand.java │ ├── WikiCommand.java │ └── YoutubeCommand.java │ ├── persistence │ ├── dao │ │ ├── ConnerieDAO.java │ │ ├── GenericDAO.java │ │ ├── QuizzDAO.java │ │ ├── QuizzMergeDAO.java │ │ ├── QuoteDAO.java │ │ ├── RSSFeedDAO.java │ │ └── SkanditeDAO.java │ └── model │ │ ├── Connerie.java │ │ ├── QuizzMergeException.java │ │ ├── QuizzMergeRequest.java │ │ ├── QuizzPlayer.java │ │ ├── Quote.java │ │ ├── RSSFeed.java │ │ └── Skandite.java │ ├── utils │ ├── BotUtilsService.java │ ├── BundleService.java │ ├── DiscordUtils.java │ ├── HashAndByteCount.java │ ├── ResourcesFactory.java │ ├── StartupBean.java │ └── URLBuilder.java │ └── web │ ├── HelpPage.css │ ├── HelpPage.html │ ├── HelpPage.java │ ├── HomePage.html │ ├── HomePage.java │ ├── QuizzMergePage.html │ ├── QuizzMergePage.java │ ├── QuizzScorePage.html │ ├── QuizzScorePage.java │ ├── TemplatePage.css │ ├── TemplatePage.html │ ├── TemplatePage.java │ ├── TemplatePage.js │ ├── WicketApplication.java │ ├── auth │ ├── LoggedInButtonPanel.html │ ├── LoggedInButtonPanel.java │ ├── LoggedOutButtonPanel.html │ ├── LoggedOutButtonPanel.java │ ├── LoginPage.html │ ├── LoginPage.java │ ├── LoginPanel.html │ ├── LoginPanel.java │ ├── LogoutPage.java │ └── WicketSession.java │ ├── components │ ├── BootstrapFeedbackPanel.css │ ├── BootstrapFeedbackPanel.java │ ├── ChosenBehavior.java │ └── references │ │ ├── ChosenReference.java │ │ ├── chosen-sprite.png │ │ ├── chosen.css │ │ └── chosen.jquery.min.js │ ├── nav │ ├── NavigationHeader.html │ ├── NavigationHeader.java │ ├── NavigationItem.html │ └── NavigationItem.java │ └── utils │ ├── ModelFactory.java │ └── WicketUtils.java ├── resources ├── META-INF │ ├── persistence.xml │ └── services │ │ └── javax.enterprise.inject.spi.Extension ├── config.properties.example ├── log4j.properties ├── quizz.txt └── twitch.properties.example └── webapp ├── WEB-INF ├── beans.xml └── web.xml ├── favicon.ico └── images ├── accept.png └── cross.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Local config files 2 | src/main/resources/config.properties 3 | src/main/resources/own3d.properties 4 | 5 | #log files 6 | geekbot.log 7 | derby.log 8 | 9 | # Maven build directory 10 | target 11 | 12 | # Eclipse files 13 | .project 14 | .classpath 15 | .settings 16 | .factorypath 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | - openjdk11 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ------------ 3 | 4 | [![Build Status](https://secure.travis-ci.org/Athou/GeekBot.png?branch=master)](http://travis-ci.org/Athou/GeekBot) 5 | 6 | GeekBot is an IRC Bot and a framework built around the excellent PircBot Java IRC Bot library. It is made for deployment on OpenShift but should work on a standalone JBossAS 7.x server and probably on any JavaEE6 container. 7 | 8 | The bot is bundled with plenty of commands already, and allows you to create new commands easily with annotations. 9 | 10 | One of the features of the bot is to store sentences said on the channel and give those back at some point in the future randomly in a conversation or when directly addressed. 11 | 12 | There are also basic commands like Quotes handling, Google Web and Images search, Wikipedia search, Youtube title fetching when linking a video, Horoscope, RSS Fetching, Imgur images mirroring, and much more. 13 | 14 | All commands are available in the be.hehehe.geekbot.commands package and you can add your own. 15 | 16 | 17 | Installation 18 | ------------ 19 | 20 | Copy `/src/main/resources/config.properties.example` to `$OPENSHIFT_DATA_DIR/config.properties` 21 | 22 | Copy `/src/main/resources/own3d.properties.example` to `$OPENSHIFT_DATA_DIR/own3d.properties` 23 | 24 | Edit those new files to reflect your local configuration (bot name, irc server and channel, database connection ...) 25 | 26 | 27 | Deployment 28 | ---------- 29 | 30 | just `git push` to your OpenShift git repository 31 | -------------------------------------------------------------------------------- /maven/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Athou/GeekBot/0c9f05db02537cdc2ea0ddbdc5bd2d34b7b71c9d/maven/maven-wrapper.jar -------------------------------------------------------------------------------- /maven/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Maven download properties 2 | #Sat Jul 04 09:06:32 CEST 2015 3 | distributionUrl=https\://repository.apache.org/content/repositories/releases/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip 4 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | wdir=$(cd "$wdir/.."; pwd) 204 | if [ -d "$wdir"/.mvn ] ; then 205 | basedir=$wdir 206 | break 207 | fi 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER="org.apache.maven.wrapper.MavenWrapperMain" 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 232 | -classpath \ 233 | "$MAVEN_PROJECTBASEDIR/maven/maven-wrapper.jar" \ 234 | ${WRAPPER_LAUNCHER} "$@" 235 | -------------------------------------------------------------------------------- /mvnw.bat: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 38 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 39 | 40 | @REM set %HOME% to equivalent of $HOME 41 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 42 | 43 | @REM Execute a user defined script before this one 44 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 45 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 46 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 47 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 48 | :skipRcPre 49 | 50 | @setlocal 51 | 52 | set ERROR_CODE=0 53 | 54 | @REM To isolate internal variables from possible post scripts, we use another setlocal 55 | @setlocal 56 | 57 | @REM ==== START VALIDATION ==== 58 | if not "%JAVA_HOME%" == "" goto OkJHome 59 | 60 | echo. 61 | echo Error: JAVA_HOME not found in your environment. >&2 62 | echo Please set the JAVA_HOME variable in your environment to match the >&2 63 | echo location of your Java installation. >&2 64 | echo. 65 | goto error 66 | 67 | :OkJHome 68 | if exist "%JAVA_HOME%\bin\java.exe" goto init 69 | 70 | echo. 71 | echo Error: JAVA_HOME is set to an invalid directory. >&2 72 | echo JAVA_HOME = "%JAVA_HOME%" >&2 73 | echo Please set the JAVA_HOME variable in your environment to match the >&2 74 | echo location of your Java installation. >&2 75 | echo. 76 | goto error 77 | 78 | :init 79 | 80 | set MAVEN_CMD_LINE_ARGS=%* 81 | 82 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 83 | @REM Fallback to current working directory if not found. 84 | 85 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 86 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 87 | 88 | set EXEC_DIR=%CD% 89 | set WDIR=%EXEC_DIR% 90 | :findBaseDir 91 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 92 | cd .. 93 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 94 | set WDIR=%CD% 95 | goto findBaseDir 96 | 97 | :baseDirFound 98 | set MAVEN_PROJECTBASEDIR=%WDIR% 99 | cd "%EXEC_DIR%" 100 | goto endDetectBaseDir 101 | 102 | :baseDirNotFound 103 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 104 | cd "%EXEC_DIR%" 105 | 106 | :endDetectBaseDir 107 | 108 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 109 | 110 | @setlocal EnableExtensions EnableDelayedExpansion 111 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 112 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 113 | 114 | :endReadAdditionalConfig 115 | 116 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 117 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\maven\maven-wrapper.jar" 118 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 119 | %MAVEN_JAVA_EXE% -Dmaven.multiModuleProjectDirectory="" %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 120 | 121 | if ERRORLEVEL 1 goto error 122 | goto end 123 | 124 | :error 125 | set ERROR_CODE=1 126 | 127 | :end 128 | @endlocal & set ERROR_CODE=%ERROR_CODE% 129 | 130 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 131 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 132 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 133 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 134 | :skipRcPost 135 | 136 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 137 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 138 | 139 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 140 | 141 | exit /B %ERROR_CODE% 142 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | be.hehehe 6 | GeekBot 7 | 2.0.0 8 | war 9 | 10 | GeekBot 11 | https://github.com/Athou/GeekBot 12 | GeekBot is an IRC Bot and a framework built around the excellent PircBot Java IRC Bot library. 13 | 14 | 15 | UTF-8 16 | 6.4.0 17 | 2.7.0.Final 18 | 1.8 19 | 1.8 20 | 21 | 22 | 23 | 24 | Athou 25 | 26 | 27 | 28 | 29 | https://github.com/Athou/GeekBot 30 | scm:git:http://github.com/Athou/GeekBot.git 31 | 32 | 33 | 34 | GitHub issues 35 | https://github.com/Athou/GeekBot/issues 36 | 37 | 38 | travis-ci 39 | http://travis-ci.org/#!/Athou/GeekBot 40 | 41 | 42 | 43 | 44 | dv8fromtheworld-maven 45 | https://m2.dv8tion.net/releases 46 | 47 | 48 | 49 | 50 | 51 | 52 | io.thorntail 53 | bom-all 54 | ${thorntail.version} 55 | import 56 | pom 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | io.thorntail 65 | thorntail-maven-plugin 66 | ${thorntail.version} 67 | 68 | 69 | 70 | package 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | src/main/resources 80 | true 81 | 82 | 83 | src/main/java 84 | 85 | **/* 86 | 87 | 88 | **/*.java 89 | 90 | 91 | 92 | 93 | 94 | src/test/resources 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.projectlombok 103 | lombok 104 | 1.18.14 105 | provided 106 | 107 | 108 | javax 109 | javaee-api 110 | 7.0 111 | provided 112 | 113 | 114 | io.thorntail 115 | jpa 116 | 117 | 118 | io.thorntail 119 | cdi 120 | 121 | 122 | io.thorntail 123 | ejb 124 | 125 | 126 | io.thorntail 127 | undertow 128 | 129 | 130 | io.thorntail 131 | logging 132 | 133 | 134 | 135 | com.google.guava 136 | guava 137 | 18.0 138 | 139 | 140 | commons-beanutils 141 | commons-beanutils 142 | 1.8.3 143 | 144 | 145 | commons-codec 146 | commons-codec 147 | 1.7 148 | 149 | 150 | commons-collections 151 | commons-collections 152 | 3.2.1 153 | 154 | 155 | commons-io 156 | commons-io 157 | 2.4 158 | 159 | 160 | org.apache.commons 161 | commons-lang3 162 | 3.5 163 | 164 | 165 | org.json 166 | json 167 | 20160810 168 | 169 | 170 | net.java.dev.rome 171 | rome 172 | 1.0.0 173 | 174 | 175 | org.jsoup 176 | jsoup 177 | 1.7.1 178 | 179 | 180 | org.jdom 181 | jdom 182 | 2.0.2 183 | 184 | 185 | org.apache.wicket 186 | wicket-core 187 | ${wicket.version} 188 | 189 | 190 | org.apache.wicket 191 | wicket-auth-roles 192 | ${wicket.version} 193 | 194 | 195 | org.apache.wicket 196 | wicket-extensions 197 | ${wicket.version} 198 | 199 | 200 | org.apache.wicket 201 | wicket-bootstrap 202 | 0.5 203 | 204 | 205 | org.apache.wicket 206 | wicket-cdi 207 | ${wicket.version} 208 | 209 | 210 | org.odlabs.wiquery 211 | wiquery-core 212 | 6.2.0 213 | 214 | 215 | com.googlecode.lambdaj 216 | lambdaj 217 | 2.3.3 218 | 219 | 220 | org.twitter4j 221 | twitter4j-core 222 | 4.0.4 223 | 224 | 225 | com.google.apis 226 | google-api-services-youtube 227 | v3-rev138-1.20.0 228 | 229 | 230 | org.ocpsoft.prettytime 231 | prettytime 232 | 3.2.7.Final 233 | 234 | 235 | net.dv8tion 236 | JDA 237 | 4.3.0_339 238 | 239 | 240 | club.minnced 241 | opus-java 242 | 243 | 244 | 245 | 246 | mysql 247 | mysql-connector-java 248 | 6.0.6 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /src/main/java/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Athou/GeekBot/0c9f05db02537cdc2ea0ddbdc5bd2d34b7b71c9d/src/main/java/.gitkeep -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/annotations/BotCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | import javax.enterprise.context.ApplicationScoped; 9 | 10 | /** 11 | * 12 | * Annotate any class to indicate that it contains trigger commands. 13 | * 14 | */ 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target(ElementType.TYPE) 17 | @ApplicationScoped 18 | public @interface BotCommand { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/annotations/Help.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | * Annotate a method (already annotated with @Trigger) to specify its help string. 11 | * 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.METHOD) 15 | public @interface Help { 16 | 17 | /** 18 | * The string that will be printed when help is requested. 19 | * 20 | */ 21 | public String value() default ""; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/annotations/RandomAction.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | * Will execute this method randomly when someone speaks on the chan. The value is a proc percentage (default: 1%). 11 | * 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.METHOD) 15 | public @interface RandomAction { 16 | /** 17 | * Proc percentage (default: 1%). 18 | * 19 | */ 20 | public int value() default 1; 21 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/annotations/TimedAction.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * 11 | * Execute a command repeatedly. Set the value in minutes. Default is 60 minutes. 12 | * 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.METHOD) 16 | public @interface TimedAction { 17 | public int value() default 60; 18 | 19 | public TimeUnit timeUnit() default TimeUnit.MINUTES; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/annotations/Trigger.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | * Annotate a method (from a class annotated with @BotCommand) to indicate it is a trigger. A trigger method can return a String or a List 11 | * . 12 | * 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.METHOD) 16 | public @interface Trigger { 17 | /** 18 | * The string that will trigger the command. 19 | * 20 | */ 21 | public String value() default ""; 22 | 23 | /** 24 | * The trigger type. 25 | * 26 | */ 27 | public TriggerType type() default be.hehehe.geekbot.annotations.TriggerType.EXACTMATCH; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/annotations/TriggerType.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.annotations; 2 | 3 | /** 4 | * 5 | * Trigger types. 6 | * 7 | */ 8 | public enum TriggerType { 9 | /** 10 | * Triggers if the whole message matches the trigger. 11 | */ 12 | EXACTMATCH, 13 | /** 14 | * Triggers if the message starts with the trigger. 15 | */ 16 | STARTSWITH, 17 | /** 18 | * Triggers if the message contains the nick name of the bot 19 | */ 20 | BOTNAME, 21 | /** 22 | * Triggers on everything 23 | */ 24 | EVERYTHING; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/annotations/Triggers.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | import javax.inject.Qualifier; 9 | 10 | @Qualifier 11 | @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD }) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Triggers { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/CommandInvoker.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.concurrent.Future; 5 | 6 | import javax.ejb.AccessTimeout; 7 | import javax.ejb.AsyncResult; 8 | import javax.ejb.Asynchronous; 9 | import javax.ejb.Lock; 10 | import javax.ejb.LockType; 11 | import javax.ejb.Singleton; 12 | import javax.enterprise.inject.Instance; 13 | import javax.inject.Inject; 14 | 15 | import lombok.extern.jbosslog.JBossLog; 16 | 17 | @Singleton 18 | @JBossLog 19 | public class CommandInvoker { 20 | 21 | @Inject 22 | Instance container; 23 | 24 | @Asynchronous 25 | @Lock(LockType.READ) 26 | @AccessTimeout(-1) 27 | public Future invoke(Method method, Object... args) { 28 | Object result = null; 29 | 30 | log.debug("Invoking: " + method.getDeclaringClass().getSimpleName() + "#" + method.getName()); 31 | 32 | Object instance = container.select(method.getDeclaringClass()).get(); 33 | 34 | final Object commandInstance = instance; 35 | 36 | try { 37 | if (method.getParameterTypes().length == 0) { 38 | result = method.invoke(commandInstance, new Object[0]); 39 | } else { 40 | result = method.invoke(commandInstance, args); 41 | } 42 | } catch (Exception e) { 43 | log.error(e.getMessage(), e); 44 | } 45 | log.debug("Done invoking: " + method.getDeclaringClass().getSimpleName() + "#" + method.getName()); 46 | return new AsyncResult<>(result); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/DiscordBot.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import javax.security.auth.login.LoginException; 8 | 9 | import lombok.extern.jbosslog.JBossLog; 10 | import net.dv8tion.jda.api.JDA; 11 | import net.dv8tion.jda.api.JDABuilder; 12 | import net.dv8tion.jda.api.entities.Guild; 13 | import net.dv8tion.jda.api.entities.TextChannel; 14 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 15 | import net.dv8tion.jda.api.hooks.ListenerAdapter; 16 | 17 | @JBossLog 18 | public class DiscordBot extends ListenerAdapter { 19 | 20 | private final String botName; 21 | private final MessageListener listener; 22 | 23 | private JDA jda; 24 | private Guild guild; 25 | 26 | public DiscordBot(String token, String botName, MessageListener listener) { 27 | this.botName = botName; 28 | this.listener = listener; 29 | try { 30 | jda = JDABuilder.createDefault(token).addEventListeners(this).build().awaitReady(); 31 | guild = jda.getGuilds().get(0); 32 | } catch (LoginException | InterruptedException e) { 33 | log.error(e.getMessage(), e); 34 | } 35 | } 36 | 37 | @Override 38 | public void onMessageReceived(MessageReceivedEvent event) { 39 | String sender = event.getMember().getEffectiveName(); 40 | if (!botName.equalsIgnoreCase(sender)) { 41 | listener.onMessage(event.getTextChannel().getName(), sender, event.getMessage().getContentDisplay()); 42 | } 43 | } 44 | 45 | public interface MessageListener { 46 | void onMessage(String channel, String sender, String message); 47 | } 48 | 49 | public void sendMessage(String channel, String message) { 50 | guild.getTextChannelsByName(channel, true).get(0).sendMessage(message).queue(); 51 | } 52 | 53 | public List getUsers(String channel) { 54 | List channels = guild.getTextChannelsByName(channel, true); 55 | if (channels.isEmpty()) { 56 | 57 | return Collections.emptyList(); 58 | } 59 | 60 | return channels.get(0).getMembers().stream().map(m -> m.getNickname()).collect(Collectors.toList()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/GeekBot.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Random; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.Future; 9 | import java.util.concurrent.ScheduledExecutorService; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import javax.annotation.PostConstruct; 13 | import javax.ejb.Schedule; 14 | import javax.ejb.Singleton; 15 | import javax.enterprise.inject.Instance; 16 | import javax.enterprise.inject.Produces; 17 | import javax.inject.Inject; 18 | 19 | import org.apache.commons.lang3.StringUtils; 20 | 21 | import com.google.common.collect.Maps; 22 | 23 | import be.hehehe.geekbot.annotations.RandomAction; 24 | import be.hehehe.geekbot.annotations.TimedAction; 25 | import be.hehehe.geekbot.annotations.Trigger; 26 | import be.hehehe.geekbot.annotations.TriggerType; 27 | import be.hehehe.geekbot.annotations.Triggers; 28 | import be.hehehe.geekbot.bot.DiscordBot.MessageListener; 29 | import be.hehehe.geekbot.utils.BotUtilsService; 30 | import be.hehehe.geekbot.utils.BundleService; 31 | import lombok.extern.jbosslog.JBossLog; 32 | 33 | @Singleton 34 | @JBossLog 35 | public class GeekBot { 36 | 37 | private String botName; 38 | private List channels; 39 | 40 | private List triggers; 41 | private List randoms; 42 | 43 | @Inject 44 | GeekBotCDIExtension extension; 45 | 46 | private DiscordBot bot; 47 | 48 | @Inject 49 | CommandInvoker invoker; 50 | 51 | @Inject 52 | BundleService bundleService; 53 | 54 | @Inject 55 | BotUtilsService utilsService; 56 | 57 | @Inject 58 | Instance container; 59 | 60 | private ScheduledExecutorService scheduler; 61 | private Map timers = Maps.newConcurrentMap(); 62 | 63 | @PostConstruct 64 | public void init() { 65 | 66 | scheduler = Executors.newScheduledThreadPool(50); 67 | 68 | botName = bundleService.getBotName(); 69 | channels = bundleService.getChannels(); 70 | 71 | triggers = extension.getTriggers(); 72 | randoms = extension.getRandoms(); 73 | setTimers(extension.getTimers()); 74 | 75 | boolean connect = !bundleService.isTest(); 76 | 77 | final String discordBotName = bundleService.getDiscordBotName(); 78 | 79 | if (connect) { 80 | String token = bundleService.getDiscordToken(); 81 | bot = new DiscordBot(token, botName, new MessageListener() { 82 | 83 | @Override 84 | public void onMessage(String channel, String sender, String message) { 85 | if (StringUtils.startsWith(sender, discordBotName)) { 86 | int start = message.indexOf('<'); 87 | int end = message.indexOf('>'); 88 | 89 | sender = message.substring(start + 1, end); 90 | message = message.substring(end + 2); 91 | } 92 | handlePossibleTrigger(channel, message, sender); 93 | } 94 | }); 95 | } 96 | } 97 | 98 | public void setTimers(List methods) { 99 | Long now = System.currentTimeMillis(); 100 | for (Method method : methods) { 101 | timers.put(method, now); 102 | } 103 | } 104 | 105 | @Schedule(hour = "*", minute = "*", persistent = false) 106 | public void timer() { 107 | Long now = System.currentTimeMillis(); 108 | for (String channel : channels) { 109 | for (final Method method : timers.keySet()) { 110 | if (isMethodAllowedToRun(channel, method)) { 111 | int interval = method.getAnnotation(TimedAction.class).value(); 112 | TimeUnit timeUnit = method.getAnnotation(TimedAction.class).timeUnit(); 113 | 114 | if (now - timers.get(method) > timeUnit.toMillis(interval)) { 115 | timers.put(method, now); 116 | invokeTrigger(channel, method); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * Handle each message and check for an action 125 | * 126 | * @param message 127 | * @param author 128 | */ 129 | private void handlePossibleTrigger(String channel, String message, String author) { 130 | 131 | for (Method triggerMethod : triggers) { 132 | 133 | if (isMethodAllowedToRun(channel, triggerMethod)) { 134 | 135 | Trigger trig = triggerMethod.getAnnotation(Trigger.class); 136 | String trigger = trig.value(); 137 | String triggerStartsWith = trigger + " "; 138 | 139 | TriggerEvent triggerEvent = null; 140 | switch (trig.type()) { 141 | 142 | case EXACTMATCH: 143 | if (StringUtils.equals(trigger, message)) { 144 | triggerEvent = buildEvent(channel, message, author, trigger); 145 | } 146 | break; 147 | case STARTSWITH: 148 | if (StringUtils.startsWith(message, triggerStartsWith)) { 149 | triggerEvent = buildEvent(channel, message, author, triggerStartsWith); 150 | } 151 | break; 152 | case BOTNAME: 153 | if (botNameInMessage(message)) { 154 | triggerEvent = buildEvent(channel, message, author, botName); 155 | } 156 | break; 157 | 158 | case EVERYTHING: 159 | if (!isMessageTrigger(message) && !botNameInMessage(message)) { 160 | triggerEvent = buildEvent(channel, message, author, null); 161 | } 162 | break; 163 | default: 164 | break; 165 | } 166 | 167 | invokeTrigger(channel, triggerMethod, triggerEvent); 168 | } 169 | } 170 | 171 | if (!isMessageTrigger(message)) { 172 | // this message is not a trigger, try to proc something randomly 173 | for (Method randomMethod : randoms) { 174 | if (isMethodAllowedToRun(channel, randomMethod)) { 175 | int rand = new Random().nextInt(100) + 1; 176 | int probability = randomMethod.getAnnotation(RandomAction.class).value(); 177 | if (rand <= probability) { 178 | invokeTrigger(channel, randomMethod, buildEvent(channel, message, author, null)); 179 | break; 180 | } 181 | } 182 | } 183 | } 184 | 185 | } 186 | 187 | private boolean isMethodAllowedToRun(String channel, Method method) { 188 | return true; 189 | } 190 | 191 | /** 192 | * 193 | * @param message 194 | * @return true if the message is a trigger 195 | */ 196 | private boolean isMessageTrigger(String message) { 197 | boolean isTrigger = false; 198 | for (Method triggerMethod : triggers) { 199 | Trigger trig = triggerMethod.getAnnotation(Trigger.class); 200 | String trigger = trig.value(); 201 | TriggerType type = trig.type(); 202 | if (type == TriggerType.EXACTMATCH || type == TriggerType.STARTSWITH) { 203 | if (StringUtils.startsWithIgnoreCase(message, trigger)) { 204 | isTrigger = true; 205 | break; 206 | } 207 | } else if (type == TriggerType.BOTNAME && botNameInMessage(message)) { 208 | isTrigger = true; 209 | break; 210 | } 211 | } 212 | return isTrigger; 213 | } 214 | 215 | /** 216 | * 217 | * @param message 218 | * @return true if the name of the bot is in the message 219 | */ 220 | private boolean botNameInMessage(String message) { 221 | return StringUtils.containsIgnoreCase(message, botName); 222 | } 223 | 224 | /** 225 | * 226 | * @param message 227 | * @return true if the msssage contains the nickname of a user on the chan 228 | */ 229 | private boolean nickInMessage(String channel, String message) { 230 | boolean nickInMessage = false; 231 | if (message != null) { 232 | String[] split = message.split(" "); 233 | for (String user : bot.getUsers(channel)) { 234 | for (String token : split) { 235 | if (token.equalsIgnoreCase(user) || token.equalsIgnoreCase(user + ":")) { 236 | nickInMessage = true; 237 | break; 238 | } 239 | } 240 | } 241 | } 242 | return nickInMessage; 243 | } 244 | 245 | public void invokeTrigger(String channel, final Method method) { 246 | invokeTrigger(channel, method, buildEvent(channel)); 247 | } 248 | 249 | private void invokeTrigger(String channel, final Method method, final TriggerEvent event) { 250 | if (event == null) { 251 | return; 252 | } 253 | Future future = invoker.invoke(method, event); 254 | 255 | try { 256 | Object result = future.get(1, TimeUnit.MINUTES); 257 | handleResultOfInvoke(channel, result); 258 | } catch (Exception e) { 259 | log.error("Error while invoking method " + method.getDeclaringClass().getSimpleName() + "#" + method.getName() + ": " 260 | + e.getMessage(), e); 261 | } 262 | 263 | } 264 | 265 | private TriggerEvent buildEvent(String channel) { 266 | return buildEvent(channel, null, null, null); 267 | } 268 | 269 | private TriggerEvent buildEvent(final String channel, String message, String author, String trigger) { 270 | List users = bot.getUsers(channel); 271 | TriggerEvent event = new TriggerEventImpl(message, author, trigger, users, utilsService.extractURL(message), 272 | nickInMessage(channel, message), botNameInMessage(message), isMessageTrigger(message), new MessageWriter() { 273 | @Override 274 | public void write(String message) { 275 | sendMessage(channel, message); 276 | } 277 | }, scheduler); 278 | return event; 279 | } 280 | 281 | /** 282 | * Handle the result of the trigger invokation 283 | * 284 | */ 285 | @SuppressWarnings("unchecked") 286 | public void handleResultOfInvoke(String channel, Object o) { 287 | if (o != null) { 288 | if (o instanceof String) { 289 | String message = (String) o; 290 | sendMessage(channel, message); 291 | } else if (o instanceof List) { 292 | List messages = (List) o; 293 | sendMessages(channel, messages); 294 | } else { 295 | sendMessage(channel, o.toString()); 296 | } 297 | } 298 | } 299 | 300 | private void sendMessages(String channel, List list) { 301 | for (String s : list) { 302 | sendMessage(channel, s); 303 | } 304 | } 305 | 306 | /** 307 | * Sends the message, splitting it if too long. 308 | * 309 | * @param message 310 | */ 311 | private void sendMessage(String channel, String message) { 312 | int limit = 400; 313 | while (message.length() > limit) { 314 | int lastSpace = message.substring(0, limit).lastIndexOf(' '); 315 | bot.sendMessage(channel, message.substring(0, lastSpace)); 316 | message = message.substring(lastSpace + 1); 317 | } 318 | bot.sendMessage(channel, message); 319 | } 320 | 321 | @Produces 322 | @Triggers 323 | public List getTriggers() { 324 | return triggers; 325 | } 326 | 327 | } 328 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/GeekBotCDIExtension.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.List; 5 | 6 | import javax.enterprise.event.Observes; 7 | import javax.enterprise.inject.spi.Extension; 8 | import javax.enterprise.inject.spi.ProcessAnnotatedType; 9 | 10 | import be.hehehe.geekbot.annotations.BotCommand; 11 | import be.hehehe.geekbot.annotations.RandomAction; 12 | import be.hehehe.geekbot.annotations.TimedAction; 13 | import be.hehehe.geekbot.annotations.Trigger; 14 | 15 | import com.google.common.collect.Lists; 16 | 17 | /** 18 | * Utility extension for scanning triggers and actions. 19 | * 20 | * 21 | */ 22 | public class GeekBotCDIExtension implements Extension { 23 | 24 | List triggers = Lists.newArrayList(); 25 | List randoms = Lists.newArrayList(); 26 | List timers = Lists.newArrayList(); 27 | 28 | public void processAnnotatedType(@Observes ProcessAnnotatedType pat) { 29 | Class klass = pat.getAnnotatedType().getJavaClass(); 30 | if (klass.isAnnotationPresent(BotCommand.class)) { 31 | for (Method m : klass.getMethods()) { 32 | if (m.isAnnotationPresent(Trigger.class)) { 33 | triggers.add(m); 34 | } 35 | if (m.isAnnotationPresent(RandomAction.class)) { 36 | randoms.add(m); 37 | } 38 | if (m.isAnnotationPresent(TimedAction.class)) { 39 | timers.add(m); 40 | } 41 | } 42 | } 43 | } 44 | 45 | public List getTriggers() { 46 | return triggers; 47 | } 48 | 49 | public List getRandoms() { 50 | return randoms; 51 | } 52 | 53 | public List getTimers() { 54 | return timers; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/MessageWriter.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | public interface MessageWriter { 4 | void write(String message); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/State.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | /** 4 | * Injectable class serving as a temporary in-memory storage. Commands may store objects in this structure for later use instead of using 5 | * static fields. 6 | * 7 | * 8 | */ 9 | public interface State { 10 | 11 | void put(Object key, Object value); 12 | 13 | Object get(Object key); 14 | 15 | T get(Object key, Class klass); 16 | 17 | void put(Object value); 18 | 19 | Object get(); 20 | 21 | T get(Class klass); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/StateImpl.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.util.Map; 4 | 5 | import javax.enterprise.inject.Alternative; 6 | 7 | import com.google.common.collect.Maps; 8 | 9 | @Alternative 10 | public class StateImpl implements State { 11 | 12 | private Map state = Maps.newHashMap(); 13 | private Object singleObject; 14 | 15 | @Override 16 | public void put(Object key, Object value) { 17 | state.put(key, value); 18 | } 19 | 20 | @Override 21 | public Object get(Object key) { 22 | return state.get(key); 23 | } 24 | 25 | @Override 26 | @SuppressWarnings("unchecked") 27 | public T get(Object key, Class klass) { 28 | return (T) get(key); 29 | } 30 | 31 | @Override 32 | public void put(Object value) { 33 | singleObject = value; 34 | } 35 | 36 | @Override 37 | public Object get() { 38 | return singleObject; 39 | } 40 | 41 | @Override 42 | @SuppressWarnings("unchecked") 43 | public T get(Class klass) { 44 | return (T) singleObject; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/StateProducer.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.util.Map; 4 | 5 | import javax.enterprise.context.ApplicationScoped; 6 | import javax.enterprise.inject.Produces; 7 | import javax.enterprise.inject.spi.InjectionPoint; 8 | 9 | import com.google.common.collect.Maps; 10 | 11 | @ApplicationScoped 12 | public class StateProducer { 13 | 14 | private Map states = Maps.newHashMap(); 15 | 16 | @Produces 17 | public State getState(InjectionPoint injectionPoint) { 18 | String className = injectionPoint.getMember().getDeclaringClass().getName(); 19 | State state = states.get(className); 20 | if (state == null) { 21 | state = new StateImpl(); 22 | states.put(className, state); 23 | } 24 | return state; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/TriggerEvent.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.util.Collection; 4 | import java.util.concurrent.ScheduledExecutorService; 5 | 6 | public interface TriggerEvent { 7 | 8 | /** 9 | * Returns the message that triggered this command, unaltered. 10 | * 11 | * @return the original message, null for methods annotated with TimedAction 12 | */ 13 | String getOriginalMessage(); 14 | 15 | /** 16 | * Returns the message that triggered this command, without the trigger. 17 | * 18 | * @return the the message without the trigger, null for methods annotated with TimedAction 19 | */ 20 | String getMessage(); 21 | 22 | /** 23 | * Returns the name of the author of the message; 24 | * 25 | * @return the nick name of the author, null for methods annotated with TimedAction 26 | */ 27 | String getAuthor(); 28 | 29 | /** 30 | * Returns a list of users on the channel 31 | * 32 | * @return a list of users 33 | */ 34 | Collection getUsers(); 35 | 36 | /** 37 | * Returns true if the message contains an url. 38 | * 39 | * @return true if an url was found in the message. 40 | */ 41 | boolean hasURL(); 42 | 43 | /** 44 | * Returns the first url found in the message. 45 | * 46 | * @return the first url found in the message. 47 | */ 48 | String getURL(); 49 | 50 | /** 51 | * Returns true if the message contains a nickname from one of the users on the chan 52 | * 53 | * @return true if someone was highlighted, false if not or for methods annotated with TimedAction 54 | */ 55 | boolean isNickInMessage(); 56 | 57 | /** 58 | * Returns true if the name of the bot is in the message 59 | * 60 | * @return true if the bot was highlighted, false if not or for methods annotated with TimedAction 61 | */ 62 | boolean isBotInMessage(); 63 | 64 | /** 65 | * Returns true if the message starts with an existing trigger. Useful for commands with TriggerType.EVERYTHING 66 | * 67 | * @return true if the message starts with an existing trigger registered in the bot. 68 | */ 69 | boolean isStartsWithTrigger(); 70 | 71 | /** 72 | * Writes something directly on the channel. Usefull to notify progress. 73 | * 74 | * @param message 75 | * the message sent to the channel 76 | */ 77 | void write(String message); 78 | 79 | /** 80 | * Get the common scheduler pool instead of creating yours. 81 | * 82 | * @return the common scheduler pool 83 | */ 84 | ScheduledExecutorService getScheduler(); 85 | 86 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/bot/TriggerEventImpl.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.bot; 2 | 3 | import java.util.Collection; 4 | import java.util.concurrent.ScheduledExecutorService; 5 | 6 | public class TriggerEventImpl implements TriggerEvent { 7 | private String message; 8 | private String author; 9 | private String messageWithoutTrigger; 10 | private Collection users; 11 | private String url; 12 | private boolean nickInMessage; 13 | private boolean botInMessage; 14 | private boolean startsWithTrigger; 15 | private MessageWriter messageWriter; 16 | private ScheduledExecutorService scheduler; 17 | 18 | /** 19 | * use only in tests, some fields are not set 20 | * 21 | * @param messageWithoutTrigger 22 | */ 23 | public TriggerEventImpl(String messageWithoutTrigger) { 24 | this(messageWithoutTrigger, null, null); 25 | } 26 | 27 | /** 28 | * use only in tests, some fields are not set 29 | * 30 | * @param messageWithoutTrigger 31 | * @param url 32 | */ 33 | public TriggerEventImpl(String messageWithoutTrigger, String url) { 34 | this(messageWithoutTrigger, url, null); 35 | } 36 | 37 | /** 38 | * use only in tests, some fields are not set 39 | * 40 | * @param messageWithoutTrigger 41 | */ 42 | public TriggerEventImpl(String messageWithoutTrigger, String url, String author) { 43 | this.messageWithoutTrigger = messageWithoutTrigger; 44 | this.url = url; 45 | this.author = author; 46 | } 47 | 48 | public TriggerEventImpl(String message, String author, String trigger, Collection users, String url, boolean nickInMessage, 49 | boolean botInMessage, boolean startsWithTrigger, MessageWriter messageWriter, ScheduledExecutorService scheduler) { 50 | this.message = message; 51 | this.author = author; 52 | this.users = users; 53 | this.url = url; 54 | this.nickInMessage = nickInMessage; 55 | this.botInMessage = botInMessage; 56 | this.startsWithTrigger = startsWithTrigger; 57 | 58 | if (message != null && trigger != null) { 59 | this.messageWithoutTrigger = message.replace(trigger, ""); 60 | } else { 61 | this.messageWithoutTrigger = message; 62 | } 63 | this.messageWriter = messageWriter; 64 | this.scheduler = scheduler; 65 | 66 | } 67 | 68 | @Override 69 | public String getOriginalMessage() { 70 | return message; 71 | } 72 | 73 | @Override 74 | public String getAuthor() { 75 | return author; 76 | } 77 | 78 | @Override 79 | public Collection getUsers() { 80 | return users; 81 | } 82 | 83 | @Override 84 | public boolean hasURL() { 85 | return url != null; 86 | } 87 | 88 | @Override 89 | public String getURL() { 90 | return url; 91 | } 92 | 93 | @Override 94 | public boolean isNickInMessage() { 95 | return nickInMessage; 96 | } 97 | 98 | @Override 99 | public boolean isBotInMessage() { 100 | return botInMessage; 101 | } 102 | 103 | @Override 104 | public String getMessage() { 105 | return messageWithoutTrigger; 106 | } 107 | 108 | @Override 109 | public boolean isStartsWithTrigger() { 110 | return startsWithTrigger; 111 | } 112 | 113 | @Override 114 | public void write(String message) { 115 | messageWriter.write(message); 116 | } 117 | 118 | @Override 119 | public ScheduledExecutorService getScheduler() { 120 | return scheduler; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/BlagueCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.inject.Inject; 7 | 8 | import org.apache.commons.lang3.StringEscapeUtils; 9 | import org.jsoup.Jsoup; 10 | import org.jsoup.nodes.Document; 11 | import org.jsoup.nodes.TextNode; 12 | 13 | import be.hehehe.geekbot.annotations.BotCommand; 14 | import be.hehehe.geekbot.annotations.Help; 15 | import be.hehehe.geekbot.annotations.Trigger; 16 | import be.hehehe.geekbot.utils.BotUtilsService; 17 | import be.hehehe.geekbot.utils.DiscordUtils; 18 | import lombok.extern.jbosslog.JBossLog; 19 | 20 | /** 21 | * Fetches a random blague from la-blague-du-jour.com (French) 22 | * 23 | * 24 | */ 25 | @BotCommand 26 | @JBossLog 27 | public class BlagueCommand { 28 | 29 | @Inject 30 | BotUtilsService utilsService; 31 | 32 | @Trigger("!blague") 33 | @Help("Fetches a random blague from humour-blague.com") 34 | public List getRandomBlague() { 35 | List result = new ArrayList<>(); 36 | String url = "http://humour-blague.com/blagues-2/index.php"; 37 | try { 38 | Document doc = Jsoup.parse(utilsService.getContent(url)); 39 | 40 | List nodes = doc.select(".blague").get(0).textNodes(); 41 | for (TextNode node : nodes) { 42 | result.add(StringEscapeUtils.unescapeHtml4(node.text())); 43 | } 44 | 45 | if (result.size() > 7) { 46 | // joke too long 47 | return getRandomBlague(); 48 | } 49 | 50 | result.add(0, DiscordUtils.bold("Mega Vanne")); 51 | result.add(DiscordUtils.bold("http://www.instantrimshot.com/")); 52 | } catch (Exception e) { 53 | log.error(e.getMessage(), e); 54 | result.add("Could not contact " + url); 55 | } 56 | 57 | return result; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/BuzzCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.net.URL; 4 | import java.util.ArrayList; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | import javax.inject.Inject; 9 | 10 | import com.sun.syndication.feed.synd.SyndEntry; 11 | import com.sun.syndication.feed.synd.SyndFeed; 12 | import com.sun.syndication.io.SyndFeedInput; 13 | import com.sun.syndication.io.XmlReader; 14 | 15 | import be.hehehe.geekbot.annotations.BotCommand; 16 | import be.hehehe.geekbot.annotations.Help; 17 | import be.hehehe.geekbot.annotations.Trigger; 18 | import be.hehehe.geekbot.persistence.dao.RSSFeedDAO; 19 | import be.hehehe.geekbot.persistence.model.RSSFeed; 20 | import be.hehehe.geekbot.utils.BotUtilsService; 21 | import be.hehehe.geekbot.utils.DiscordUtils; 22 | import lombok.extern.jbosslog.JBossLog; 23 | 24 | /** 25 | * Fetches a random post from jeanmarcmorandini.com (French) 26 | * 27 | */ 28 | @BotCommand 29 | @JBossLog 30 | public class BuzzCommand { 31 | 32 | @Inject 33 | BotUtilsService utilsService; 34 | 35 | @Inject 36 | RSSFeedDAO dao; 37 | 38 | @SuppressWarnings("unchecked") 39 | @Trigger("!buzz") 40 | @Help("Fetches one of the latest posts from jeanmarcmorandini.com") 41 | public List getLatestBuzz() { 42 | List toReturn = new ArrayList<>(); 43 | try { 44 | URL url = new URL("http://feeds.feedburner.com/jeanmarcmorandini/pExM?format=xml"); 45 | SyndFeedInput input = new SyndFeedInput(); 46 | SyndFeed rss = input.build(new XmlReader(url)); 47 | 48 | Iterator it = rss.getEntries().iterator(); 49 | String message = null; 50 | while (it.hasNext()) { 51 | SyndEntry item = it.next(); 52 | String guid = item.getUri(); 53 | RSSFeed buzz = dao.findByGUID(guid); 54 | if (buzz == null) { 55 | buzz = new RSSFeed(); 56 | buzz.setGuid(item.getUri()); 57 | dao.save(buzz); 58 | String urlBitly = utilsService.bitly(item.getLink()); 59 | message = DiscordUtils.bold("EXCLU!") + " " + item.getTitle() + " - " + urlBitly; 60 | toReturn.add(message); 61 | break; 62 | } 63 | } 64 | 65 | if (message == null) { 66 | toReturn.add("Pas d'exclus pour le moment."); 67 | } 68 | 69 | } catch (Exception e) { 70 | log.error(e.getMessage(), e); 71 | } 72 | 73 | return toReturn; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/ConnerieCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.inject.Inject; 7 | 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | import be.hehehe.geekbot.annotations.BotCommand; 11 | import be.hehehe.geekbot.annotations.Help; 12 | import be.hehehe.geekbot.annotations.RandomAction; 13 | import be.hehehe.geekbot.annotations.Trigger; 14 | import be.hehehe.geekbot.annotations.TriggerType; 15 | import be.hehehe.geekbot.bot.TriggerEvent; 16 | import be.hehehe.geekbot.persistence.dao.ConnerieDAO; 17 | import be.hehehe.geekbot.persistence.model.Connerie; 18 | import be.hehehe.geekbot.utils.DiscordUtils; 19 | 20 | /** 21 | * Stores all lines spoken on the channel. The bot will also give one of those sentences back when addressed or randomly in a conversation. 22 | * 23 | * 24 | */ 25 | @BotCommand 26 | public class ConnerieCommand { 27 | 28 | @Inject 29 | ConnerieDAO dao; 30 | 31 | @Trigger(type = TriggerType.EVERYTHING) 32 | public List storeEveryLines(TriggerEvent event) { 33 | String message = event.getMessage(); 34 | List result = new ArrayList(); 35 | if (!event.hasURL()) { 36 | if (!event.isNickInMessage() && message.length() > 9 && !message.contains("<") && !message.contains(">") 37 | && !event.isStartsWithTrigger()) { 38 | Connerie connerie = new Connerie(event.getAuthor(), message); 39 | dao.save(connerie); 40 | } 41 | } 42 | 43 | return result; 44 | } 45 | 46 | @RandomAction(3) 47 | @Trigger(type = TriggerType.BOTNAME) 48 | public String getRandomLine() { 49 | return dao.getRandom().getValue(); 50 | } 51 | 52 | @Trigger(value = "!rand", type = TriggerType.STARTSWITH) 53 | @Help("Returns a random sentence matching given arguments.") 54 | public String getRandQuote(TriggerEvent event) { 55 | String r = null; 56 | String keywords = event.getMessage(); 57 | if (StringUtils.isNotBlank(keywords)) { 58 | keywords = keywords.trim(); 59 | if (keywords.length() > 1) { 60 | Connerie connerie = dao.getRandomMatching(keywords.split(" ")); 61 | if (connerie != null) { 62 | r = connerie.getValue(); 63 | } 64 | } 65 | } 66 | return r; 67 | } 68 | 69 | @Trigger(value = "!stat", type = TriggerType.STARTSWITH) 70 | @Help("Count sentences containing the given arguments in our database.") 71 | public String getStatCount(TriggerEvent event) { 72 | String r = null; 73 | String keywords = event.getMessage(); 74 | if (StringUtils.isNotBlank(keywords)) { 75 | keywords = keywords.trim(); 76 | if (keywords.length() > 1) { 77 | int count = dao.getCountMatching(keywords.split(" ")); 78 | r = DiscordUtils.bold("Stat count for \"" + keywords + "\" : ") + count; 79 | } 80 | } 81 | return r; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/DansTonChatCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.List; 4 | 5 | import javax.inject.Inject; 6 | 7 | import org.apache.commons.lang3.SystemUtils; 8 | import org.jdom2.Document; 9 | import org.jdom2.Element; 10 | 11 | import com.google.common.collect.Lists; 12 | 13 | import be.hehehe.geekbot.annotations.BotCommand; 14 | import be.hehehe.geekbot.annotations.Trigger; 15 | import be.hehehe.geekbot.utils.BotUtilsService; 16 | import be.hehehe.geekbot.utils.DiscordUtils; 17 | import lombok.extern.jbosslog.JBossLog; 18 | 19 | @BotCommand 20 | @JBossLog 21 | public class DansTonChatCommand { 22 | 23 | // key from android apk 24 | private static final String API_URL = "http://api.danstonchat.com/0.2/view/random0?key=cad048f021e9413b"; 25 | 26 | @Inject 27 | BotUtilsService utilsService; 28 | 29 | @Trigger("!dtc") 30 | public List getRandom() { 31 | List list = Lists.newArrayList(); 32 | try { 33 | String xml = utilsService.getContent(API_URL); 34 | Document doc = utilsService.parseXML(xml); 35 | Element item = null; 36 | for (Element element : doc.getRootElement().getChildren("item")) { 37 | String content = element.getChildText("content"); 38 | if (content.split(SystemUtils.LINE_SEPARATOR).length <= 5) { 39 | item = element; 40 | break; 41 | } 42 | } 43 | 44 | if (item == null) { 45 | throw new RuntimeException("No item found."); 46 | } 47 | 48 | String content = item.getChildText("content"); 49 | String upvote = item.getChildText("vote_plus"); 50 | String downvote = item.getChildText("vote_minus"); 51 | for (String line : content.split(SystemUtils.LINE_SEPARATOR)) { 52 | list.add(line); 53 | } 54 | list.add(DiscordUtils.bold("LOL je sé pa ékrir MDR") + String.format(" (+%s/-%s)", upvote, downvote)); 55 | } catch (Exception e) { 56 | list.add("Could not contact DTC."); 57 | log.error(e.getMessage(), e); 58 | } 59 | return list; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/EpisodesCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.net.URLEncoder; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.ArrayList; 7 | import java.util.Date; 8 | import java.util.List; 9 | import java.util.Locale; 10 | 11 | import javax.inject.Inject; 12 | 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.json.JSONArray; 15 | import org.json.JSONException; 16 | import org.json.JSONObject; 17 | import org.ocpsoft.prettytime.PrettyTime; 18 | 19 | import be.hehehe.geekbot.annotations.BotCommand; 20 | import be.hehehe.geekbot.annotations.Help; 21 | import be.hehehe.geekbot.annotations.Trigger; 22 | import be.hehehe.geekbot.annotations.TriggerType; 23 | import be.hehehe.geekbot.bot.TriggerEvent; 24 | import be.hehehe.geekbot.utils.BotUtilsService; 25 | import be.hehehe.geekbot.utils.DiscordUtils; 26 | import lombok.extern.jbosslog.JBossLog; 27 | 28 | /** 29 | * Finds out the next air date of tv shows. 30 | * 31 | */ 32 | @BotCommand 33 | @JBossLog 34 | public class EpisodesCommand { 35 | 36 | @Inject 37 | BotUtilsService utilsService; 38 | 39 | @Trigger(value = "!next", type = TriggerType.STARTSWITH) 40 | @Help("Information about a TV show from TVRage.com") 41 | public List getNextEpisode(TriggerEvent event) { 42 | String seriesName = event.getMessage(); 43 | 44 | List list = new ArrayList<>(); 45 | try { 46 | String url = String.format("http://api.tvmaze.com/singlesearch/shows?q=%s&embed=episodes", 47 | URLEncoder.encode(seriesName, "UTF-8")); 48 | String content = utilsService.getContent(url); 49 | JSONObject root = new JSONObject(content); 50 | JSONObject embedded = root.getJSONObject("_embedded"); 51 | JSONArray episodes = embedded.getJSONArray("episodes"); 52 | 53 | JSONObject previous = null; 54 | JSONObject next = null; 55 | Date now = new Date(); 56 | for (int i = 0; i < episodes.length(); i++) { 57 | JSONObject episode = episodes.getJSONObject(i); 58 | Date airdate = parseAirDate(episode); 59 | if (airdate != null) { 60 | if (airdate.before(now)) { 61 | previous = episode; 62 | } else if (next == null) { 63 | next = episode; 64 | } 65 | } 66 | } 67 | 68 | list.add(DiscordUtils.bold(root.getString("name")) + " http://www.imdb.com/title/" 69 | + root.getJSONObject("externals").getString("imdb")); 70 | if (next != null) { 71 | list.add(DiscordUtils.bold("Next Episode: ") + parseEpisode(next)); 72 | } else { 73 | list.add(DiscordUtils.bold("Next Episode: ") + "N/A"); 74 | } 75 | if (previous != null) { 76 | list.add(DiscordUtils.bold("Previous Episode: ") + parseEpisode(previous)); 77 | } else { 78 | list.add(DiscordUtils.bold("Previous Episode: ") + "N/A"); 79 | } 80 | } catch (Exception e) { 81 | log.error(e.getMessage(), e); 82 | } 83 | return list; 84 | } 85 | 86 | public String parseEpisode(JSONObject episode) throws JSONException, ParseException { 87 | List parts = new ArrayList<>(); 88 | parts.add(String.format("S%02dE%02d", episode.getInt("season"), episode.getInt("number"))); 89 | parts.add(" - " + episode.getString("name")); 90 | 91 | Date airdate = parseAirDate(episode); 92 | parts.add("(" + new SimpleDateFormat("yyyy-MM-dd HH:mm").format(airdate)); 93 | 94 | PrettyTime prettyTime = new PrettyTime(Locale.FRENCH); 95 | parts.add(", " + prettyTime.format(airdate) + ")"); 96 | 97 | return StringUtils.join(parts, " "); 98 | } 99 | 100 | private Date parseAirDate(JSONObject episode) throws JSONException, ParseException { 101 | if (episode.isNull("airstamp")) 102 | return null; 103 | return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse(episode.getString("airstamp")); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/GoogleCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.net.URLDecoder; 4 | import java.net.URLEncoder; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import javax.inject.Inject; 9 | 10 | import org.apache.commons.lang3.StringEscapeUtils; 11 | import org.json.JSONArray; 12 | import org.json.JSONObject; 13 | 14 | import be.hehehe.geekbot.annotations.BotCommand; 15 | import be.hehehe.geekbot.annotations.Help; 16 | import be.hehehe.geekbot.annotations.Trigger; 17 | import be.hehehe.geekbot.annotations.TriggerType; 18 | import be.hehehe.geekbot.bot.TriggerEvent; 19 | import be.hehehe.geekbot.utils.BotUtilsService; 20 | import be.hehehe.geekbot.utils.BundleService; 21 | import be.hehehe.geekbot.utils.DiscordUtils; 22 | import lombok.extern.jbosslog.JBossLog; 23 | 24 | /** 25 | * Google search, web or images 26 | * 27 | */ 28 | @BotCommand 29 | @JBossLog 30 | public class GoogleCommand { 31 | 32 | @Inject 33 | BotUtilsService utilsService; 34 | 35 | @Inject 36 | BundleService bundleService; 37 | 38 | public enum Mode { 39 | WEB("web"), IMAGE("images"); 40 | 41 | private Mode(String mode) { 42 | this.mode = mode; 43 | } 44 | 45 | private String mode; 46 | 47 | @Override 48 | public String toString() { 49 | return mode; 50 | } 51 | } 52 | 53 | @Trigger(value = "!google", type = TriggerType.STARTSWITH) 54 | @Help("Google search.") 55 | public List google(TriggerEvent event) { 56 | return google(event.getMessage(), Mode.WEB); 57 | } 58 | 59 | @Trigger(value = "!image", type = TriggerType.STARTSWITH) 60 | @Help("Google Image search.") 61 | public List image(TriggerEvent event) { 62 | return google(event.getMessage(), Mode.IMAGE); 63 | } 64 | 65 | public List google(String keywords, Mode mode) { 66 | String result = ""; 67 | try { 68 | String key = bundleService.getGoogleKey(); 69 | String cx = bundleService.getGoogleCseId(); 70 | 71 | String apiUrl = "https://www.googleapis.com/customsearch/v1?key=" + key + "&cx=" + cx; 72 | if (mode == Mode.IMAGE) { 73 | apiUrl += "&searchType=image"; 74 | } 75 | String url = apiUrl + "&q=" + URLEncoder.encode(keywords, "UTF-8"); 76 | result = utilsService.getContent(url); 77 | } catch (Exception e) { 78 | log.error(e.getMessage(), e); 79 | } 80 | return parse(result, keywords, mode); 81 | } 82 | 83 | private List parse(String source, String keywords, Mode mode) { 84 | List result = new ArrayList<>(); 85 | try { 86 | JSONObject json = new JSONObject(source); 87 | 88 | JSONArray ja = json.getJSONArray("items"); 89 | JSONObject j = ja.getJSONObject(0); 90 | if (mode == Mode.WEB) { 91 | String firstLine = StringEscapeUtils.unescapeHtml4(DiscordUtils.bold(j.getString("title"))) + " - " 92 | + URLDecoder.decode(j.getString("link"), "UTF-8"); 93 | 94 | result.add(firstLine); 95 | 96 | String content = j.getString("snippet"); 97 | content = content.replaceAll("\n", ""); 98 | 99 | result.add(content); 100 | } else { 101 | String s = json.getJSONArray("items").getJSONObject(0).getString("link"); 102 | result.add(DiscordUtils.bold(s)); 103 | } 104 | 105 | } catch (Exception e) { 106 | result.add("Your search - " + keywords + " - did not match any documents."); 107 | } 108 | 109 | return result; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.LinkedHashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | import javax.inject.Inject; 9 | 10 | import org.apache.commons.lang3.StringUtils; 11 | 12 | import be.hehehe.geekbot.annotations.BotCommand; 13 | import be.hehehe.geekbot.annotations.Help; 14 | import be.hehehe.geekbot.annotations.Trigger; 15 | import be.hehehe.geekbot.annotations.TriggerType; 16 | import be.hehehe.geekbot.annotations.Triggers; 17 | import be.hehehe.geekbot.bot.TriggerEvent; 18 | import be.hehehe.geekbot.utils.BundleService; 19 | import be.hehehe.geekbot.utils.DiscordUtils; 20 | 21 | import com.google.common.collect.Lists; 22 | 23 | /** 24 | * Prints help 25 | * 26 | */ 27 | @BotCommand 28 | public class HelpCommand { 29 | 30 | @Inject 31 | @Triggers 32 | List triggers; 33 | 34 | @Inject 35 | BundleService bundle; 36 | 37 | @Trigger(value = "!help") 38 | @Help("Prints all triggers.") 39 | public List helpGeneral() { 40 | List result = Lists.newArrayList(); 41 | result.add(DiscordUtils.bold("Triggers: ")); 42 | Set set = new LinkedHashSet(); 43 | for (Method m : triggers) { 44 | Trigger trigger = m.getAnnotation(Trigger.class); 45 | TriggerType type = trigger.type(); 46 | if (type == TriggerType.EXACTMATCH || type == TriggerType.STARTSWITH) { 47 | set.add(trigger.value().trim()); 48 | } 49 | } 50 | result.add(StringUtils.join(set, " ")); 51 | result.add("See also: " + bundle.getWebServerRootPath() + "/help"); 52 | return result; 53 | } 54 | 55 | @Trigger(value = "!help", type = TriggerType.STARTSWITH) 56 | public List helpCommand(TriggerEvent event) { 57 | List result = Lists.newArrayList(); 58 | for (Method m : triggers) { 59 | if (m.isAnnotationPresent(Help.class)) { 60 | Trigger trigger = m.getAnnotation(Trigger.class); 61 | String help = m.getAnnotation(Help.class).value(); 62 | if (StringUtils.equals(event.getMessage().trim(), trigger.value()) && StringUtils.isNotBlank(help)) { 63 | StringBuilder line = new StringBuilder(trigger.value()); 64 | if (trigger.type() == TriggerType.STARTSWITH) { 65 | line.append(" "); 66 | } 67 | line.append(" : "); 68 | result.add(DiscordUtils.bold(line.toString()) + help); 69 | } 70 | } 71 | } 72 | return result; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/HoroscopeCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.Map; 5 | 6 | import javax.inject.Inject; 7 | 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.apache.commons.lang3.SystemUtils; 10 | import org.jsoup.Jsoup; 11 | import org.jsoup.nodes.Document; 12 | import org.jsoup.nodes.Element; 13 | 14 | import com.google.common.collect.Maps; 15 | 16 | import be.hehehe.geekbot.annotations.BotCommand; 17 | import be.hehehe.geekbot.annotations.Help; 18 | import be.hehehe.geekbot.annotations.Trigger; 19 | import be.hehehe.geekbot.annotations.TriggerType; 20 | import be.hehehe.geekbot.bot.TriggerEvent; 21 | import be.hehehe.geekbot.utils.BotUtilsService; 22 | import be.hehehe.geekbot.utils.DiscordUtils; 23 | import lombok.extern.jbosslog.JBossLog; 24 | 25 | /** 26 | * Horoscope from astrocenter.fr (French) 27 | * 28 | */ 29 | @BotCommand 30 | @JBossLog 31 | public class HoroscopeCommand { 32 | 33 | @Inject 34 | BotUtilsService utilsService; 35 | 36 | private final Map mapping = buildMapping(); 37 | 38 | private Map buildMapping() { 39 | Map mapping = Maps.newLinkedHashMap(); 40 | mapping.put("belier", "belier"); 41 | mapping.put("bélier", "belier"); 42 | mapping.put("taureau", "taureau"); 43 | mapping.put("gemeaux", "gemeaux"); 44 | mapping.put("gémeaux", "gemeaux"); 45 | mapping.put("cancer", "cancer"); 46 | mapping.put("lion", "lion"); 47 | mapping.put("vierge", "vierge"); 48 | mapping.put("balance", "balance"); 49 | mapping.put("scorpion", "scorpion"); 50 | mapping.put("sagittaire", "sagittaire"); 51 | mapping.put("capricorne", "capricorne"); 52 | mapping.put("verseau", "verseau"); 53 | mapping.put("poisson", "poissons"); 54 | mapping.put("poissons", "poissons"); 55 | return mapping; 56 | } 57 | 58 | @Trigger(value = "!horoscope", type = TriggerType.EXACTMATCH) 59 | @Help("Prints help on how to use this command.") 60 | 61 | public String getHoroscopeHelp() { 62 | String availableSigns = StringUtils.join(mapping.keySet(), ", "); 63 | return DiscordUtils.bold("!horoscope ") + " - Available signs : " + availableSigns; 64 | } 65 | 66 | @Trigger(value = "!horoscope", type = TriggerType.STARTSWITH) 67 | public String getHoroscope(TriggerEvent event) { 68 | String sign = event.getMessage(); 69 | 70 | String content = null; 71 | String line = null; 72 | try { 73 | String id = mapping.get(sign); 74 | if (id == null) { 75 | return null; 76 | } 77 | 78 | String url = String.format("https://www.mon-horoscope-du-jour.com/horoscopes/quotidien/%s.htm", id); 79 | content = utilsService.getContent(url, StandardCharsets.ISO_8859_1.name()); 80 | Document doc = Jsoup.parse(content); 81 | 82 | Element title = doc.select("h2:contains(Notre conseil du jour)").first(); 83 | Element horo = title.parent().child(2); 84 | line = horo.text(); 85 | 86 | } catch (Exception e) { 87 | log.error("Could not parse HTML" + e.getMessage() + SystemUtils.LINE_SEPARATOR + content, e); 88 | } 89 | 90 | return line; 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/IMDBCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.net.URLDecoder; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import javax.inject.Inject; 8 | 9 | import org.apache.commons.lang3.StringEscapeUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.json.JSONArray; 12 | import org.json.JSONObject; 13 | 14 | import be.hehehe.geekbot.annotations.BotCommand; 15 | import be.hehehe.geekbot.annotations.Help; 16 | import be.hehehe.geekbot.annotations.Trigger; 17 | import be.hehehe.geekbot.annotations.TriggerType; 18 | import be.hehehe.geekbot.bot.TriggerEvent; 19 | import be.hehehe.geekbot.commands.GoogleCommand.Mode; 20 | import be.hehehe.geekbot.utils.BotUtilsService; 21 | import be.hehehe.geekbot.utils.DiscordUtils; 22 | import lombok.extern.jbosslog.JBossLog; 23 | 24 | /** 25 | * IMDB commands 26 | * 27 | */ 28 | @BotCommand 29 | @JBossLog 30 | public class IMDBCommand { 31 | 32 | @Inject 33 | BotUtilsService utilsService; 34 | 35 | @Inject 36 | GoogleCommand googleCommand; 37 | 38 | @Trigger(value = "!imdb", type = TriggerType.STARTSWITH) 39 | @Help("IMDb Movie search.") 40 | public List getResult(TriggerEvent event) { 41 | String keywords = event.getMessage(); 42 | List googleResult = googleCommand.google("site:http://www.imdb.com/title " + keywords, Mode.WEB); 43 | String[] split = googleResult.get(0).split("/"); 44 | String imdbID = null; 45 | 46 | for (String s : split) { 47 | if (s.startsWith("tt")) { 48 | imdbID = s; 49 | break; 50 | } 51 | } 52 | 53 | String result = utilsService.getContent("http://app.imdb.com/title/maindetails?tconst=" + imdbID); 54 | return parse(result); 55 | } 56 | 57 | private List parse(String source) { 58 | List result = new ArrayList<>(); 59 | 60 | try { 61 | JSONObject json = new JSONObject(source).getJSONObject("data"); 62 | 63 | String title = json.getString("title"); 64 | String url = "http://www.imdb.com/title/" + json.getString("tconst") + "/"; 65 | String year = json.getString("year"); 66 | 67 | String s = DiscordUtils.bold(title); 68 | if (!"null".equals(year)) { 69 | s += " (" + year + ")"; 70 | } 71 | 72 | s += " - " + URLDecoder.decode(url, "UTF-8"); 73 | result.add(s); 74 | s = ""; 75 | 76 | if (!json.isNull("tagline")) { 77 | String plot = json.getString("tagline"); 78 | result.add(DiscordUtils.bold("Plot: ") + plot); 79 | } 80 | s = ""; 81 | if (!json.isNull("rating") && !json.isNull("num_votes")) { 82 | String rating = json.getString("rating"); 83 | String votes = json.getString("num_votes"); 84 | s = DiscordUtils.bold("Rating: ") + rating + "/10 - " + votes + " votes - "; 85 | } 86 | 87 | if (!json.isNull("genres")) { 88 | List genres = new ArrayList<>(); 89 | JSONArray genresArray = json.getJSONArray("genres"); 90 | for (int i = 0; i < genresArray.length(); i++) { 91 | String genre = (String) genresArray.get(i); 92 | genres.add(genre); 93 | 94 | } 95 | s += StringUtils.join(genres, ", "); 96 | } 97 | result.add(s); 98 | s = ""; 99 | if (!json.isNull("directors_summary")) { 100 | List directors = new ArrayList<>(); 101 | 102 | for (int i = 0; i < json.getJSONArray("directors_summary").length(); i++) { 103 | JSONObject j2 = (JSONObject) json.getJSONArray("directors_summary").get(i); 104 | directors.add(j2.getJSONObject("name").getString("name")); 105 | 106 | } 107 | s = DiscordUtils.bold("Directed by: "); 108 | for (String director : directors) { 109 | s += director + ", "; 110 | } 111 | s = s.substring(0, s.length() - 2); 112 | result.add(s); 113 | } 114 | s = ""; 115 | if (!json.isNull("cast_summary")) { 116 | 117 | List actors = new ArrayList<>(); 118 | 119 | for (int i = 0; i < json.getJSONArray("cast_summary").length() && i < 5; i++) { 120 | JSONObject j2 = (JSONObject) json.getJSONArray("cast_summary").get(i); 121 | actors.add(j2.getJSONObject("name").getString("name")); 122 | 123 | } 124 | s = DiscordUtils.bold("Actors: "); 125 | for (String actor : actors) { 126 | s += actor + ", "; 127 | } 128 | s = s.substring(0, s.length() - 2); 129 | result.add(s); 130 | } 131 | 132 | } catch (Exception e) { 133 | log.error(e.getMessage(), e); 134 | } 135 | 136 | List result2 = new ArrayList<>(); 137 | for (String s : result) { 138 | result2.add(StringEscapeUtils.unescapeHtml4(s)); 139 | } 140 | 141 | return result2; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/MemeCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLEncoder; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | import javax.annotation.PostConstruct; 12 | import javax.inject.Inject; 13 | 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.json.JSONArray; 16 | import org.json.JSONObject; 17 | 18 | import com.google.common.collect.Lists; 19 | 20 | import be.hehehe.geekbot.annotations.BotCommand; 21 | import be.hehehe.geekbot.annotations.Help; 22 | import be.hehehe.geekbot.annotations.TimedAction; 23 | import be.hehehe.geekbot.annotations.Trigger; 24 | import be.hehehe.geekbot.annotations.TriggerType; 25 | import be.hehehe.geekbot.bot.TriggerEvent; 26 | import be.hehehe.geekbot.persistence.dao.ConnerieDAO; 27 | import be.hehehe.geekbot.persistence.model.Connerie; 28 | import be.hehehe.geekbot.utils.BotUtilsService; 29 | import be.hehehe.geekbot.utils.BundleService; 30 | import be.hehehe.geekbot.utils.DiscordUtils; 31 | import lombok.extern.jbosslog.JBossLog; 32 | 33 | @BotCommand 34 | @JBossLog 35 | public class MemeCommand { 36 | 37 | private static final String CREATE_URL = "http://version1.api.memegenerator.net/Instance_Create?username=%s&password=%s&languageCode=en&generatorID=%s&imageID=%s&text0=%s&text1=%s"; 38 | private static final String POPULAR_GENERATORS = "http://version1.api.memegenerator.net/Generators_Select_ByPopular?pageIndex=%d&pageSize=24&days=3"; 39 | 40 | @Inject 41 | ConnerieDAO dao; 42 | 43 | @Inject 44 | Random random; 45 | 46 | @Inject 47 | BundleService bundle; 48 | 49 | @Inject 50 | BotUtilsService utilsService; 51 | 52 | private List generators = Lists.newCopyOnWriteArrayList(); 53 | 54 | @PostConstruct 55 | private void init() { 56 | // y u no 57 | generators.add(new Generator("2", "166088")); 58 | // most interesting man on earth 59 | generators.add(new Generator("74", "2485")); 60 | // o'rly 61 | generators.add(new Generator("920", "117049")); 62 | // * all the things 63 | generators.add(new Generator("6013", "1121885")); 64 | // good news everyone 65 | generators.add(new Generator("1591", "112464")); 66 | // fry not sure if 67 | generators.add(new Generator("305", "84688")); 68 | // yo dawg 69 | generators.add(new Generator("79", "108785")); 70 | // one does not simply 71 | generators.add(new Generator("274947", "1865027")); 72 | // nailed it 73 | generators.add(new Generator("121", "1031")); 74 | // too damn 75 | generators.add(new Generator("998", "203665")); 76 | 77 | for (int i = 0; i < 6; i++) { 78 | addPopular(i); 79 | } 80 | } 81 | 82 | private void addPopular(int index) { 83 | try { 84 | String content = utilsService.getContent(String.format(POPULAR_GENERATORS, index)); 85 | JSONObject json = new JSONObject(content); 86 | JSONArray results = json.getJSONArray("result"); 87 | for (int i = 0; i < results.length(); i++) { 88 | JSONObject result = results.getJSONObject(i); 89 | String generatorId = String.valueOf(result.getInt("generatorID")); 90 | 91 | String imageUrl = result.getString("imageUrl"); 92 | String[] tokens = imageUrl.split(Pattern.quote("/")); 93 | String imageName = tokens[tokens.length - 1]; 94 | String imageId = imageName.split(Pattern.quote("."))[0]; 95 | 96 | generators.add(new Generator(generatorId, imageId)); 97 | } 98 | } catch (Exception e) { 99 | log.error(e.getMessage(), e); 100 | } 101 | } 102 | 103 | @TimedAction(value = 24, timeUnit = TimeUnit.HOURS) 104 | private void refreshGenerators() { 105 | generators.clear(); 106 | init(); 107 | } 108 | 109 | @Trigger(type = TriggerType.STARTSWITH, value = "!meme") 110 | @Help("Generates a meme using the first two arguments. e.g. !meme hello world, !meme \"hello world\" \"what's up?\".") 111 | public String generateWithArgument(TriggerEvent event) { 112 | String result = null; 113 | 114 | String message = event.getMessage(); 115 | List tokens = getTokens(message); 116 | 117 | if (StringUtils.isBlank(message)) { 118 | result = generateWithoutArgument(); 119 | } else if (tokens.size() == 1) { 120 | result = generate(tokens.get(0), null); 121 | } else if (tokens.size() > 1) { 122 | result = generate(tokens.get(0), tokens.get(1)); 123 | } 124 | return result; 125 | } 126 | 127 | @Trigger("!meme") 128 | @Help("Randomly generates a meme") 129 | public String generateWithoutArgument() { 130 | return generate(null, null); 131 | } 132 | 133 | public String generate(String term1, String term2) { 134 | String result = null; 135 | 136 | try { 137 | int index = random.nextInt(generators.size()); 138 | Generator generator = generators.get(index); 139 | 140 | String text0 = getRandomText(term1); 141 | String text1 = getRandomText(term2); 142 | 143 | String generatorId = generator.getGeneratorId(); 144 | String imageId = generator.getImageId(); 145 | 146 | String login = bundle.getMemeGeneratorLogin(); 147 | String password = bundle.getMemeGeneratorPassword(); 148 | 149 | String url = String.format(CREATE_URL, login, password, generatorId, imageId, text0, text1); 150 | 151 | String content = utilsService.getContent(url); 152 | 153 | JSONObject json = new JSONObject(content); 154 | JSONObject resultObject = json.getJSONObject("result"); 155 | String instanceImageUrl = resultObject.getString("instanceImageUrl"); 156 | instanceImageUrl = instanceImageUrl.replace("400x", "800x"); 157 | result = instanceImageUrl; 158 | String imgur = utilsService.mirrorImage(result); 159 | if (imgur != null) { 160 | result = imgur; 161 | } 162 | } catch (Exception e) { 163 | log.error(e.getMessage(), e); 164 | result = "Could not contact memegenerator: " + e.getMessage(); 165 | } 166 | return DiscordUtils.bold("10blague! ") + result; 167 | } 168 | 169 | private String getRandomText(String like) { 170 | String text = null; 171 | int maxLength = 50; 172 | 173 | if (like != null) { 174 | Connerie connerie = dao.getRandomMatching(maxLength, like.split(" ")); 175 | if (connerie != null) { 176 | text = connerie.getValue(); 177 | } 178 | } 179 | 180 | if (text == null) { 181 | text = dao.getRandom(maxLength).getValue(); 182 | } 183 | text = utilsService.utf8ToIso88591(text); 184 | text = utilsService.stripAccents(text); 185 | try { 186 | text = URLEncoder.encode(text, "UTF-8"); 187 | } catch (UnsupportedEncodingException e) { 188 | // do nothing 189 | } 190 | return text; 191 | } 192 | 193 | private List getTokens(String message) { 194 | List list = Lists.newArrayList(); 195 | String regex = "\"([^\"]*)\"|(\\S+)"; 196 | 197 | Matcher m = Pattern.compile(regex).matcher(message); 198 | while (m.find()) { 199 | if (m.group(1) != null) { 200 | list.add(m.group(1)); 201 | } else { 202 | list.add(m.group(2)); 203 | } 204 | } 205 | return list; 206 | } 207 | 208 | private class Generator { 209 | private String generatorId; 210 | private String imageId; 211 | 212 | public Generator(String generatorId, String imageId) { 213 | this.generatorId = generatorId; 214 | this.imageId = imageId; 215 | } 216 | 217 | public String getGeneratorId() { 218 | return generatorId; 219 | } 220 | 221 | public String getImageId() { 222 | return imageId; 223 | } 224 | 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/MirrorCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import javax.inject.Inject; 4 | 5 | import be.hehehe.geekbot.annotations.BotCommand; 6 | import be.hehehe.geekbot.annotations.Help; 7 | import be.hehehe.geekbot.annotations.Trigger; 8 | import be.hehehe.geekbot.annotations.TriggerType; 9 | import be.hehehe.geekbot.bot.State; 10 | import be.hehehe.geekbot.bot.TriggerEvent; 11 | import be.hehehe.geekbot.utils.BotUtilsService; 12 | 13 | /** 14 | * Mirrors images on imgur.com, useful for image hosts blocked at work. When the trigger is invoked without arguments, the last url on the 15 | * channel will be mirrored. 16 | * 17 | */ 18 | @BotCommand 19 | public class MirrorCommand { 20 | 21 | @Inject 22 | State state; 23 | 24 | @Inject 25 | BotUtilsService utilsService; 26 | 27 | @Trigger("!mirror") 28 | @Help("Mirrors the last link pasted on the chan.") 29 | public String getMirrorImage(TriggerEvent event) { 30 | String result = null; 31 | String lastUrl = state.get(String.class); 32 | if (lastUrl != null) { 33 | result = handleURL(lastUrl, event); 34 | } 35 | return result; 36 | } 37 | 38 | @Trigger(value = "!mirror", type = TriggerType.STARTSWITH) 39 | @Help("Mirrors the given url.") 40 | public String getMirrorImage2(TriggerEvent event) { 41 | String result = handleURL(event.getURL(), event); 42 | return result; 43 | } 44 | 45 | @Trigger(type = TriggerType.EVERYTHING) 46 | public void storeLastURL(TriggerEvent event) { 47 | if (event.hasURL()) { 48 | state.put(event.getURL()); 49 | } 50 | return; 51 | } 52 | 53 | private String handleURL(String message, TriggerEvent event) { 54 | String result = null; 55 | if (message.endsWith(".png") || message.endsWith(".jpg") || message.endsWith(".gif") || message.endsWith(".bmp")) { 56 | try { 57 | result = utilsService.mirrorImage(message); 58 | } catch (Exception e) { 59 | result = "Error retrieving file"; 60 | } 61 | } 62 | return result; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/PileoufaceCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.Random; 4 | 5 | import javax.inject.Inject; 6 | 7 | import be.hehehe.geekbot.annotations.BotCommand; 8 | import be.hehehe.geekbot.annotations.Help; 9 | import be.hehehe.geekbot.annotations.Trigger; 10 | 11 | /** 12 | * Prints either Pile or Face 13 | * 14 | */ 15 | @BotCommand 16 | public class PileoufaceCommand { 17 | 18 | @Inject 19 | Random random; 20 | 21 | @Trigger(value = "!pileouface") 22 | @Help("Prints either Pile or Face.") 23 | public String pileouface() { 24 | return random.nextBoolean() ? "Pile" : "Face"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/QuizzCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.concurrent.ScheduledFuture; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import javax.inject.Inject; 11 | 12 | import org.apache.commons.io.IOUtils; 13 | import org.apache.commons.lang3.StringUtils; 14 | 15 | import com.google.common.collect.Lists; 16 | 17 | import be.hehehe.geekbot.annotations.BotCommand; 18 | import be.hehehe.geekbot.annotations.Help; 19 | import be.hehehe.geekbot.annotations.Trigger; 20 | import be.hehehe.geekbot.annotations.TriggerType; 21 | import be.hehehe.geekbot.bot.State; 22 | import be.hehehe.geekbot.bot.TriggerEvent; 23 | import be.hehehe.geekbot.persistence.dao.QuizzDAO; 24 | import be.hehehe.geekbot.persistence.dao.QuizzMergeDAO; 25 | import be.hehehe.geekbot.persistence.model.QuizzMergeException; 26 | import be.hehehe.geekbot.utils.BotUtilsService; 27 | import be.hehehe.geekbot.utils.BundleService; 28 | import be.hehehe.geekbot.utils.DiscordUtils; 29 | import lombok.extern.jbosslog.JBossLog; 30 | 31 | /** 32 | * Quizz ! 33 | * 34 | */ 35 | @BotCommand 36 | @JBossLog 37 | public class QuizzCommand { 38 | 39 | @Inject 40 | State state; 41 | 42 | @Inject 43 | BundleService bundleService; 44 | 45 | @Inject 46 | BotUtilsService utilsService; 47 | 48 | @Inject 49 | QuizzDAO dao; 50 | 51 | @Inject 52 | QuizzMergeDAO mergeDao; 53 | 54 | private static final String ENABLED = "enabled"; 55 | private static final String QUESTIONS = "questions"; 56 | private static final String CURRENT_ANSWER = "current-answer"; 57 | private static final String TIMEOUT_TIMER = "timeout-timer"; 58 | private static final String INDICE_TIMER = "indice-timer"; 59 | private static final String NEXTQUESTION_TIMER = "nextquestion-timer"; 60 | 61 | private static final Object lock = new Object(); 62 | 63 | private static final String[] STOPWORDS = new String[] { "un", "une", "de", "des", "le", "la", "les", "en", "du" }; 64 | 65 | @Trigger(value = "!quizz") 66 | @Help("Starts the quizz.") 67 | public void startQuizz(TriggerEvent event) { 68 | if (Boolean.TRUE.equals(state.get(ENABLED))) { 69 | event.write("The quizz has already started."); 70 | } else { 71 | state.put(ENABLED, Boolean.TRUE); 72 | nextQuestion(event, 0); 73 | } 74 | } 75 | 76 | @Trigger(value = "!stop") 77 | @Help("Stops the quizz.") 78 | public void stopQuizz(TriggerEvent event) { 79 | stopIndiceTimer(); 80 | stopNextQuestionTimer(); 81 | stopTimeoutTimer(); 82 | if (Boolean.TRUE.equals(state.get(ENABLED))) { 83 | event.write("Quizz stopped."); 84 | } else { 85 | event.write("Quizz is not running."); 86 | } 87 | state.put(ENABLED, Boolean.FALSE); 88 | } 89 | 90 | @Trigger(value = "!tg") 91 | @Help("Stops the quizz.") 92 | public void stopQuizz2(TriggerEvent event) { 93 | stopQuizz(event); 94 | } 95 | 96 | private void nextQuestion(final TriggerEvent event, int delay) { 97 | state.put(CURRENT_ANSWER, null); 98 | Runnable thread = new Runnable() { 99 | @Override 100 | public void run() { 101 | try { 102 | List lines = getQuestions(); 103 | int rand = new Random().nextInt(lines.size()); 104 | 105 | String line = lines.get(rand); 106 | // removes the question from the list so that we don't get 107 | // the same question again (during this session at least) 108 | lines.remove(rand); 109 | String[] split = line.split("\\\\"); 110 | 111 | String question = split[0].trim(); 112 | String answer = split[1].trim(); 113 | 114 | state.put(CURRENT_ANSWER, answer); 115 | 116 | event.write(DiscordUtils.bold("Question! ") + question); 117 | 118 | startIndiceTimer(event); 119 | startTimeoutTimer(event); 120 | 121 | } catch (Exception e) { 122 | log.error(e.getMessage(), e); 123 | } 124 | } 125 | }; 126 | ScheduledFuture timer = event.getScheduler().schedule(thread, delay, TimeUnit.SECONDS); 127 | state.put(NEXTQUESTION_TIMER, timer); 128 | 129 | } 130 | 131 | private void stopNextQuestionTimer() { 132 | ScheduledFuture timer = state.get(NEXTQUESTION_TIMER, ScheduledFuture.class); 133 | if (timer != null) { 134 | timer.cancel(true); 135 | } 136 | } 137 | 138 | private void startIndiceTimer(final TriggerEvent event) { 139 | Runnable thread = new Runnable() { 140 | @Override 141 | public void run() { 142 | event.write(DiscordUtils.bold("1 10: " + computeIndice(state.get(CURRENT_ANSWER, String.class)))); 143 | } 144 | 145 | }; 146 | ScheduledFuture timer = event.getScheduler().schedule(thread, 20, TimeUnit.SECONDS); 147 | state.put(INDICE_TIMER, timer); 148 | } 149 | 150 | private void stopIndiceTimer() { 151 | ScheduledFuture timer = state.get(INDICE_TIMER, ScheduledFuture.class); 152 | if (timer != null) { 153 | timer.cancel(true); 154 | } 155 | } 156 | 157 | private void startTimeoutTimer(final TriggerEvent event) { 158 | Runnable thread = new Runnable() { 159 | @Override 160 | public void run() { 161 | event.write(DiscordUtils.bold("Trop tard! ") + "La réponse était: " + state.get(CURRENT_ANSWER) 162 | + ". Prochaine question dans quelques secondes ..."); 163 | nextQuestion(event, 10); 164 | } 165 | }; 166 | ScheduledFuture timer = event.getScheduler().schedule(thread, 30, TimeUnit.SECONDS); 167 | state.put(TIMEOUT_TIMER, timer); 168 | } 169 | 170 | private void stopTimeoutTimer() { 171 | ScheduledFuture timer = state.get(TIMEOUT_TIMER, ScheduledFuture.class); 172 | if (timer != null) { 173 | timer.cancel(true); 174 | } 175 | } 176 | 177 | @Trigger(type = TriggerType.EVERYTHING) 178 | public void handlePossibleAnswer(TriggerEvent event) { 179 | synchronized (lock) { 180 | String answer = state.get(CURRENT_ANSWER, String.class); 181 | if (answer != null && Boolean.TRUE.equals(state.get(ENABLED)) && !event.isStartsWithTrigger()) { 182 | if (matches(event.getMessage(), answer)) { 183 | stopIndiceTimer(); 184 | stopTimeoutTimer(); 185 | event.write(DiscordUtils.bold("Bien joué " + event.getAuthor() + " ! ") + "La réponse était: " + answer 186 | + ". Prochaine question dans quelques secondes ..."); 187 | dao.giveOnePoint(event.getAuthor()); 188 | nextQuestion(event, 10); 189 | } 190 | } 191 | } 192 | } 193 | 194 | public boolean matches(String proposition, String answer) { 195 | 196 | // check if those are numbers 197 | try { 198 | double p = Double.parseDouble(proposition.replace(" ", "")); 199 | double a = Double.parseDouble(answer.replace(" ", "")); 200 | if (p == a) { 201 | return true; 202 | } else { 203 | return false; 204 | } 205 | } catch (NumberFormatException e) { 206 | // not numbers, continue 207 | } 208 | 209 | proposition = normalize(proposition); 210 | answer = normalize(answer); 211 | 212 | // check if we have an exact match after normalization 213 | if (StringUtils.equals(proposition, answer)) { 214 | return true; 215 | } 216 | 217 | // be permissive regarding the answer (experimental). Seems to work 218 | // quite well 219 | if (answer.length() >= 4) { 220 | int diff = (answer.length() / 4); 221 | if (StringUtils.getLevenshteinDistance(proposition, answer) <= diff) { 222 | return true; 223 | } 224 | } 225 | 226 | return false; 227 | 228 | } 229 | 230 | public String computeIndice(String answer) { 231 | int ratio = 3; 232 | 233 | char c = '.'; 234 | int length = answer.length(); 235 | Random random = new Random(); 236 | 237 | char[] indice = new char[length]; 238 | Arrays.fill(indice, c); 239 | 240 | // fill spaces 241 | for (int i = 0; i < length; i++) { 242 | if (answer.charAt(i) == ' ' || answer.charAt(i) == '\'') { 243 | indice[i] = answer.charAt(i); 244 | } 245 | } 246 | 247 | // add some letters 248 | for (int i = 0; i < length / ratio + 1; i++) { 249 | int rand = random.nextInt(length); 250 | // if spot is already filled, choose next available spot 251 | while (indice[rand] != c) { 252 | rand = (rand + 1) % length; 253 | } 254 | indice[rand] = answer.charAt(rand); 255 | } 256 | 257 | return new String(indice); 258 | } 259 | 260 | private String normalize(String source) { 261 | source = source.replace("L'", ""); 262 | source = source.replace("l'", ""); 263 | source = StringUtils.trimToEmpty(utilsService.stripAccents(source)).toUpperCase(); 264 | 265 | List dest = Lists.newArrayList(); 266 | for (String word : Arrays.asList(source.split(" "))) { 267 | boolean isStopword = false; 268 | for (String stopword : STOPWORDS) { 269 | if (stopword.equalsIgnoreCase(word)) { 270 | isStopword = true; 271 | break; 272 | } 273 | } 274 | if (!isStopword) { 275 | dest.add(word); 276 | } 277 | } 278 | return StringUtils.join(dest, " "); 279 | } 280 | 281 | @SuppressWarnings("unchecked") 282 | private List getQuestions() throws IOException { 283 | List lines = state.get(QUESTIONS, List.class); 284 | if (lines == null) { 285 | lines = IOUtils.readLines(getClass().getResourceAsStream("/quizz.txt"), "UTF-8"); 286 | state.put(QUESTIONS, lines); 287 | } 288 | return lines; 289 | } 290 | 291 | @Trigger("!score") 292 | @Help("Prints Quizz scoreboard URL.") 293 | public String score(TriggerEvent event) { 294 | return DiscordUtils.bold("Scoreboard: ") + bundleService.getWebServerRootPath() + "/quizz"; 295 | } 296 | 297 | @Trigger(value = "!score merge", type = TriggerType.STARTSWITH) 298 | @Help("Introduces a merge request. !score merge . Deletes and gives its points to .") 299 | public String scoremerge(TriggerEvent event) { 300 | String[] args = event.getMessage().split(" "); 301 | if (args.length != 2) { 302 | return "Wrong syntax, check !help !score merge"; 303 | } 304 | try { 305 | mergeDao.add(args[0], args[1]); 306 | } catch (QuizzMergeException e) { 307 | return e.getMessage(); 308 | } 309 | 310 | return DiscordUtils.bold("Merge Request Added: ") + "check " + bundleService.getWebServerRootPath() + "/quizz"; 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/QuoteCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Random; 7 | 8 | import javax.inject.Inject; 9 | 10 | import be.hehehe.geekbot.annotations.BotCommand; 11 | import be.hehehe.geekbot.annotations.Help; 12 | import be.hehehe.geekbot.annotations.Trigger; 13 | import be.hehehe.geekbot.annotations.TriggerType; 14 | import be.hehehe.geekbot.bot.TriggerEvent; 15 | import be.hehehe.geekbot.persistence.dao.QuoteDAO; 16 | import be.hehehe.geekbot.persistence.model.Quote; 17 | import be.hehehe.geekbot.utils.DiscordUtils; 18 | 19 | /** 20 | * Quote engine. Store, find and get random quotes. 21 | * 22 | */ 23 | @BotCommand 24 | public class QuoteCommand { 25 | 26 | @Inject 27 | QuoteDAO dao; 28 | 29 | @Trigger("!quote") 30 | @Help("Prints a random quote.") 31 | public String getRandomQuote() { 32 | int rand = new Random().nextInt((int) dao.getCount()) + 1; 33 | return DiscordUtils.bold("" + rand) + ". " + dao.findByNumber(rand).getQuote(); 34 | } 35 | 36 | @Trigger(value = "!quote", type = TriggerType.STARTSWITH) 37 | @Help("Prints the specified quote.") 38 | public List getQuote(TriggerEvent event) throws NumberFormatException { 39 | String quoteIds = event.getMessage(); 40 | List quotes = new ArrayList(); 41 | String[] splitQuotes = quoteIds.split("[ ]"); 42 | for (int i = 0; i < splitQuotes.length && i <= 4; i++) { 43 | int id = Integer.parseInt(splitQuotes[i]); 44 | if (id > dao.getCount()) { 45 | throw new NumberFormatException(); 46 | } 47 | quotes.add(DiscordUtils.bold("" + id) + ". " + dao.findByNumber(id).getQuote()); 48 | } 49 | return quotes; 50 | 51 | } 52 | 53 | @Trigger(value = "!addquote", type = TriggerType.STARTSWITH) 54 | @Help("Add the quote in the system.") 55 | public String addQuote(TriggerEvent event) { 56 | dao.save(new Quote(event.getMessage())); 57 | return "Quote added: " + (dao.getCount()); 58 | } 59 | 60 | @Trigger(value = "!findquote", type = TriggerType.STARTSWITH) 61 | @Help("Prints a list of quotes matching the given keywords.") 62 | public String findQuote(TriggerEvent event) { 63 | List keywordList = Arrays.asList(event.getMessage().split("[ ]")); 64 | List quotes = dao.findByKeywords(keywordList); 65 | String result = ""; 66 | if (quotes.isEmpty()) { 67 | result = " none."; 68 | } else { 69 | for (Quote q : quotes) { 70 | result += " " + q.getNumber(); 71 | } 72 | } 73 | result = DiscordUtils.bold("Matching quotes:") + result; 74 | return result; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/ReverseCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import be.hehehe.geekbot.annotations.BotCommand; 6 | import be.hehehe.geekbot.annotations.Help; 7 | import be.hehehe.geekbot.annotations.Trigger; 8 | import be.hehehe.geekbot.annotations.TriggerType; 9 | import be.hehehe.geekbot.bot.TriggerEvent; 10 | 11 | /** 12 | * Reverse the given string. 13 | * 14 | */ 15 | @BotCommand 16 | public class ReverseCommand { 17 | 18 | @Trigger(value = "!reverse", type = TriggerType.STARTSWITH) 19 | @Help("Reverse the given string.") 20 | public String reverse(TriggerEvent e) { 21 | String message = e.getMessage(); 22 | return StringUtils.reverse(message); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/Rot13Command.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import be.hehehe.geekbot.annotations.BotCommand; 4 | import be.hehehe.geekbot.annotations.Help; 5 | import be.hehehe.geekbot.annotations.Trigger; 6 | import be.hehehe.geekbot.annotations.TriggerType; 7 | import be.hehehe.geekbot.bot.TriggerEvent; 8 | 9 | /** 10 | * ROT13 Algorithm 11 | * 12 | */ 13 | @BotCommand 14 | public class Rot13Command { 15 | 16 | @Trigger(value = "!rot13", type = TriggerType.STARTSWITH) 17 | @Help("\"Crypts\" the given sentence.") 18 | public String update(TriggerEvent event) { 19 | StringBuilder message = new StringBuilder(); 20 | 21 | for (char c : event.getMessage().toCharArray()) { 22 | if (c >= 'a' && c <= 'm') 23 | c += 13; 24 | else if (c >= 'n' && c <= 'z') 25 | c -= 13; 26 | else if (c >= 'A' && c <= 'M') 27 | c += 13; 28 | else if (c >= 'N' && c <= 'Z') 29 | c -= 13; 30 | message.append(c); 31 | } 32 | 33 | return message.toString(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/SkanditeCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | import javax.inject.Inject; 8 | 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.ocpsoft.prettytime.PrettyTime; 11 | 12 | import be.hehehe.geekbot.annotations.BotCommand; 13 | import be.hehehe.geekbot.annotations.Trigger; 14 | import be.hehehe.geekbot.annotations.TriggerType; 15 | import be.hehehe.geekbot.bot.TriggerEvent; 16 | import be.hehehe.geekbot.persistence.dao.SkanditeDAO; 17 | import be.hehehe.geekbot.persistence.model.Skandite; 18 | import be.hehehe.geekbot.utils.BotUtilsService; 19 | import be.hehehe.geekbot.utils.DiscordUtils; 20 | import be.hehehe.geekbot.utils.HashAndByteCount; 21 | 22 | /** 23 | * Stores all links and gives an alert when a link has already been posted on the chan. 24 | * 25 | */ 26 | @BotCommand 27 | public class SkanditeCommand { 28 | 29 | @Inject 30 | BotUtilsService utilsService; 31 | 32 | @Inject 33 | SkanditeDAO dao; 34 | 35 | @Trigger(type = TriggerType.EVERYTHING) 36 | public List handleSkandites(TriggerEvent event) { 37 | 38 | List result = new ArrayList<>(); 39 | if (event.hasURL()) { 40 | String url = event.getURL(); 41 | Skandite skandite = dao.findByURL(url); 42 | HashAndByteCount hashAndByteCount = null; 43 | if (skandite == null) { 44 | hashAndByteCount = utilsService.calculateHashAndByteCount(url); 45 | if (hashAndByteCount != null) { 46 | skandite = dao.findByHashAndByteCount(hashAndByteCount.getHash(), hashAndByteCount.getByteCount()); 47 | } 48 | } 49 | 50 | if (skandite != null) { 51 | if (!StringUtils.equals(skandite.getAuthor(), event.getAuthor())) { 52 | PrettyTime prettyTime = new PrettyTime(); 53 | String line = DiscordUtils.bold("Skandite! ") + DiscordUtils.linkWithoutPreview(skandite.getUrl()) + " linked " 54 | + prettyTime.format(skandite.getPostedDate()) + " by " + skandite.getAuthor() + " (" + skandite.getCount() 55 | + "x)."; 56 | result.add(line); 57 | skandite.setCount(skandite.getCount() + 1); 58 | dao.update(skandite); 59 | } 60 | } else { 61 | skandite = new Skandite(); 62 | skandite.setUrl(url); 63 | skandite.setPostedDate(new Date()); 64 | skandite.setAuthor(event.getAuthor()); 65 | skandite.setCount(1l); 66 | if (hashAndByteCount != null) { 67 | skandite.setHash(hashAndByteCount.getHash()); 68 | skandite.setByteCount(hashAndByteCount.getByteCount()); 69 | } 70 | dao.save(skandite); 71 | } 72 | 73 | } 74 | return result; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/VDMCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.List; 4 | 5 | import javax.inject.Inject; 6 | 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.jdom2.Document; 9 | import org.jdom2.Element; 10 | 11 | import com.google.common.collect.Lists; 12 | 13 | import be.hehehe.geekbot.annotations.BotCommand; 14 | import be.hehehe.geekbot.annotations.Help; 15 | import be.hehehe.geekbot.annotations.Trigger; 16 | import be.hehehe.geekbot.annotations.TriggerType; 17 | import be.hehehe.geekbot.bot.TriggerEvent; 18 | import be.hehehe.geekbot.utils.BotUtilsService; 19 | import be.hehehe.geekbot.utils.BundleService; 20 | import be.hehehe.geekbot.utils.DiscordUtils; 21 | import lombok.extern.jbosslog.JBossLog; 22 | 23 | /** 24 | * Gives back a random VDM (French) 25 | * 26 | */ 27 | @BotCommand 28 | @JBossLog 29 | public class VDMCommand { 30 | 31 | @Inject 32 | BundleService bundleService; 33 | 34 | @Inject 35 | BotUtilsService utilsService; 36 | 37 | @Trigger("!vdm") 38 | @Help("Prints a random VDM.") 39 | public List getRandomVDM() { 40 | return parseVDM("random"); 41 | 42 | } 43 | 44 | @Trigger(value = "!vdm", type = TriggerType.STARTSWITH) 45 | @Help("Prints given VDM id.") 46 | public List getRandomVDM(TriggerEvent event) { 47 | return parseVDM(event.getMessage()); 48 | 49 | } 50 | 51 | private List parseVDM(String vdmId) { 52 | List result = Lists.newArrayList(); 53 | String key = bundleService.getVDMApiKey(); 54 | if (StringUtils.isBlank(key)) { 55 | result.add("VDM API key not set."); 56 | return result; 57 | } 58 | 59 | try { 60 | String url = "http://api.betacie.com/view/" + vdmId + "/nocomment/?key=" + key + "&language=fr"; 61 | String xml = utilsService.getContent(url); 62 | Document doc = utilsService.parseXML(xml); 63 | 64 | Element root = doc.getRootElement(); 65 | Element item = root.getChild("items").getChild("item"); 66 | String text = item.getChild("text").getValue(); 67 | text = text.replaceAll("(\\r|\\n)", ""); 68 | String upvote = item.getChild("agree").getValue(); 69 | String downvote = item.getChild("deserved").getValue(); 70 | 71 | result.add(DiscordUtils.bold("VDM") + " - " + text); 72 | result.add(DiscordUtils.bold("MOAR FAKE PLZ") + String.format(" (+%s/-%s)", upvote, downvote)); 73 | 74 | } catch (Exception e) { 75 | result.add("Could not fetch a VDM for some reason."); 76 | log.error(e.getMessage(), e); 77 | } 78 | return result; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/WikiCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.List; 4 | 5 | import javax.inject.Inject; 6 | 7 | import be.hehehe.geekbot.annotations.BotCommand; 8 | import be.hehehe.geekbot.annotations.Help; 9 | import be.hehehe.geekbot.annotations.Trigger; 10 | import be.hehehe.geekbot.annotations.TriggerType; 11 | import be.hehehe.geekbot.bot.TriggerEvent; 12 | import be.hehehe.geekbot.commands.GoogleCommand.Mode; 13 | 14 | /** 15 | * Wikipedia search 16 | * 17 | */ 18 | @BotCommand 19 | public class WikiCommand { 20 | 21 | @Inject 22 | GoogleCommand googleCommand; 23 | 24 | @Trigger(value = "!wiki", type = TriggerType.STARTSWITH) 25 | @Help("Wikipedia search.") 26 | public List getWikiResults(TriggerEvent event) { 27 | return googleCommand.google("wikipedia " + event.getMessage(), Mode.WEB); 28 | } 29 | 30 | @Trigger(value = "!wikipedia", type = TriggerType.STARTSWITH) 31 | @Help("Wikipedia search.") 32 | public List getWikiResults2(TriggerEvent event) { 33 | return getWikiResults(event); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/commands/YoutubeCommand.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.commands; 2 | 3 | import java.util.List; 4 | 5 | import javax.inject.Inject; 6 | 7 | import be.hehehe.geekbot.annotations.BotCommand; 8 | import be.hehehe.geekbot.annotations.Help; 9 | import be.hehehe.geekbot.annotations.Trigger; 10 | import be.hehehe.geekbot.annotations.TriggerType; 11 | import be.hehehe.geekbot.bot.TriggerEvent; 12 | import be.hehehe.geekbot.commands.GoogleCommand.Mode; 13 | 14 | /** 15 | * Youtube search 16 | * 17 | */ 18 | @BotCommand 19 | public class YoutubeCommand { 20 | 21 | @Inject 22 | GoogleCommand googleCommand; 23 | 24 | @Trigger(value = "!youtube", type = TriggerType.STARTSWITH) 25 | @Help("Search YouTube for a video matching arguments") 26 | public List getYoutubeResults(TriggerEvent event) { 27 | return googleCommand.google("site:www.youtube.com " + event.getMessage(), Mode.WEB); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/dao/ConnerieDAO.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.dao; 2 | 3 | import java.util.List; 4 | import java.util.Random; 5 | 6 | import javax.ejb.Stateless; 7 | import javax.inject.Inject; 8 | import javax.persistence.TypedQuery; 9 | import javax.persistence.criteria.CriteriaQuery; 10 | import javax.persistence.criteria.Path; 11 | import javax.persistence.criteria.Predicate; 12 | import javax.persistence.criteria.Root; 13 | 14 | import be.hehehe.geekbot.persistence.model.Connerie; 15 | 16 | import com.google.common.collect.Iterables; 17 | import com.google.common.collect.Lists; 18 | 19 | @Stateless 20 | public class ConnerieDAO extends GenericDAO { 21 | 22 | @Inject 23 | Random random; 24 | 25 | public Connerie getRandom() { 26 | return getRandom(-1); 27 | } 28 | 29 | public Connerie getRandom(int maxLength) { 30 | Connerie connerie = null; 31 | int count = (int) getCount(maxLength); 32 | if (count > 0) { 33 | int rand = random.nextInt(count) + 1; 34 | connerie = Iterables.getOnlyElement(findAll(rand, 1, maxLength), null); 35 | } 36 | return connerie; 37 | } 38 | 39 | public Connerie getRandomMatching(String... keywords) { 40 | return getRandomMatching(-1, keywords); 41 | } 42 | 43 | public Connerie getRandomMatching(int maxLength, String... keywords) { 44 | Connerie con = getConnerie(true, maxLength, keywords); 45 | if (con == null) { 46 | con = getConnerie(false, maxLength, keywords); 47 | } 48 | return con; 49 | } 50 | 51 | public int getCountMatching(String... keywords) { 52 | List list = getConneries(true, -1, keywords); 53 | if (list == null) { 54 | list = getConneries(false, -1, keywords); 55 | } 56 | 57 | return list.size(); 58 | } 59 | 60 | private Connerie getConnerie(boolean spaces, int maxLength, String... keywords) { 61 | List list = getConneries(spaces, maxLength, keywords); 62 | Connerie con = null; 63 | if (!list.isEmpty()) { 64 | con = list.get(random.nextInt(list.size())); 65 | } 66 | return con; 67 | } 68 | 69 | private List getConneries(boolean spaces, int maxLength, String... keywords) { 70 | CriteriaQuery query = builder.createQuery(Connerie.class); 71 | Root root = query.from(Connerie.class); 72 | Path value = root.get("value"); 73 | 74 | List predicates = Lists.newArrayList(); 75 | for (String keyword : keywords) { 76 | Predicate p = null; 77 | if (spaces) { 78 | p = builder.like(builder.lower(value), "% " + keyword.toLowerCase() + " %"); 79 | } else { 80 | p = builder.like(builder.lower(value), "%" + keyword.toLowerCase() + "%"); 81 | } 82 | 83 | predicates.add(p); 84 | } 85 | if (maxLength > 0) { 86 | predicates.add(builder.lessThan(builder.length(value), maxLength)); 87 | } 88 | query.where(predicates.toArray(new Predicate[] {})); 89 | return em.createQuery(query).getResultList(); 90 | } 91 | 92 | public long getCount(int maxLength) { 93 | CriteriaQuery query = builder.createQuery(Long.class); 94 | Root root = query.from(getType()); 95 | Path value = root.get("value"); 96 | query.select(builder.count(root)); 97 | if (maxLength > 0) { 98 | query.where(builder.lessThan(builder.length(value), maxLength)); 99 | } 100 | return em.createQuery(query).getSingleResult(); 101 | } 102 | 103 | public List findAll(int startIndex, int count, int maxLength) { 104 | CriteriaQuery query = builder.createQuery(getType()); 105 | Root root = query.from(Connerie.class); 106 | Path value = root.get("value"); 107 | if (maxLength > 0) { 108 | query.where(builder.lessThan(builder.length(value), maxLength)); 109 | } 110 | TypedQuery q = em.createQuery(query); 111 | q.setMaxResults(count); 112 | q.setFirstResult(startIndex); 113 | return q.getResultList(); 114 | } 115 | 116 | @Override 117 | protected Class getType() { 118 | return Connerie.class; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/dao/GenericDAO.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.dao; 2 | 3 | import java.util.List; 4 | 5 | import javax.annotation.PostConstruct; 6 | import javax.persistence.EntityManager; 7 | import javax.persistence.PersistenceContext; 8 | import javax.persistence.TypedQuery; 9 | import javax.persistence.criteria.CriteriaBuilder; 10 | import javax.persistence.criteria.CriteriaQuery; 11 | import javax.persistence.criteria.Root; 12 | 13 | public abstract class GenericDAO { 14 | 15 | @PersistenceContext 16 | protected EntityManager em; 17 | 18 | protected CriteriaBuilder builder; 19 | 20 | @PostConstruct 21 | public void init() { 22 | builder = em.getCriteriaBuilder(); 23 | } 24 | 25 | public void save(T object) { 26 | em.persist(object); 27 | } 28 | 29 | @SuppressWarnings("unchecked") 30 | public void update(T... objects) { 31 | for (Object object : objects) { 32 | em.merge(object); 33 | } 34 | } 35 | 36 | public void delete(T object) { 37 | object = em.merge(object); 38 | em.remove(object); 39 | } 40 | 41 | public void deleteById(Object id) { 42 | Object ref = em.getReference(getType(), id); 43 | em.remove(ref); 44 | } 45 | 46 | public T findById(long id) { 47 | T t = em.find(getType(), id); 48 | return t; 49 | } 50 | 51 | public List findAll() { 52 | CriteriaBuilder builder = em.getCriteriaBuilder(); 53 | CriteriaQuery query = builder.createQuery(getType()); 54 | query.from(getType()); 55 | return em.createQuery(query).getResultList(); 56 | } 57 | 58 | public List findAll(int startIndex, int count) { 59 | 60 | CriteriaBuilder builder = em.getCriteriaBuilder(); 61 | CriteriaQuery query = builder.createQuery(getType()); 62 | query.from(getType()); 63 | TypedQuery q = em.createQuery(query); 64 | q.setMaxResults(count); 65 | q.setFirstResult(startIndex); 66 | return q.getResultList(); 67 | 68 | } 69 | 70 | public long getCount() { 71 | CriteriaBuilder builder = em.getCriteriaBuilder(); 72 | CriteriaQuery query = builder.createQuery(Long.class); 73 | Root root = query.from(getType()); 74 | query.select(builder.count(root)); 75 | return em.createQuery(query).getSingleResult(); 76 | } 77 | 78 | public List findByField(String field, Object value) { 79 | CriteriaQuery query = builder.createQuery(getType()); 80 | Root root = query.from(getType()); 81 | query.where(builder.equal(root.get(field), value)); 82 | return em.createQuery(query).getResultList(); 83 | } 84 | 85 | protected abstract Class getType(); 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/dao/QuizzDAO.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.dao; 2 | 3 | import java.util.List; 4 | 5 | import javax.ejb.Stateless; 6 | import javax.persistence.criteria.CriteriaQuery; 7 | import javax.persistence.criteria.Root; 8 | 9 | import be.hehehe.geekbot.persistence.model.QuizzPlayer; 10 | 11 | @Stateless 12 | public class QuizzDAO extends GenericDAO { 13 | 14 | public void giveOnePoint(String author) { 15 | List players = findByField("name", author); 16 | if (players.isEmpty()) { 17 | QuizzPlayer player = new QuizzPlayer(); 18 | player.setName(author); 19 | player.setPoints(1); 20 | save(player); 21 | } else { 22 | QuizzPlayer player = players.get(0); 23 | player.setPoints(player.getPoints() + 1); 24 | update(player); 25 | } 26 | } 27 | 28 | public List getPlayersOrderByPoints() { 29 | CriteriaQuery query = builder.createQuery(QuizzPlayer.class); 30 | Root root = query.from(QuizzPlayer.class); 31 | query.orderBy(builder.desc(root.get("points"))); 32 | return em.createQuery(query).getResultList(); 33 | } 34 | 35 | public List getPlayersOrderByName() { 36 | CriteriaQuery query = builder.createQuery(QuizzPlayer.class); 37 | Root root = query.from(QuizzPlayer.class); 38 | query.orderBy(builder.asc(root.get("name"))); 39 | return em.createQuery(query).getResultList(); 40 | } 41 | 42 | @Override 43 | protected Class getType() { 44 | return QuizzPlayer.class; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/dao/QuizzMergeDAO.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.dao; 2 | 3 | import javax.ejb.Stateless; 4 | import javax.inject.Inject; 5 | 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | import be.hehehe.geekbot.persistence.model.QuizzMergeException; 9 | import be.hehehe.geekbot.persistence.model.QuizzMergeRequest; 10 | import be.hehehe.geekbot.persistence.model.QuizzPlayer; 11 | 12 | import com.google.common.collect.Iterables; 13 | 14 | @Stateless 15 | public class QuizzMergeDAO extends GenericDAO { 16 | 17 | @Inject 18 | QuizzDAO quizzDao; 19 | 20 | public void add(String receiver, String giver) throws QuizzMergeException { 21 | 22 | if (StringUtils.isBlank(receiver) || StringUtils.isBlank(giver)) { 23 | throw new QuizzMergeException("Both players are required."); 24 | } 25 | 26 | if (StringUtils.equals(receiver, giver)) { 27 | throw new QuizzMergeException("Players need to be different."); 28 | } 29 | 30 | QuizzPlayer receivingPlayer = Iterables.getOnlyElement(quizzDao.findByField("name", receiver), null); 31 | QuizzPlayer givingPlayer = Iterables.getOnlyElement(quizzDao.findByField("name", giver), null); 32 | 33 | if (receivingPlayer == null) { 34 | throw new QuizzMergeException("Player not found: " + receiver); 35 | } 36 | if (givingPlayer == null) { 37 | throw new QuizzMergeException("Player not found: " + giver); 38 | } 39 | 40 | QuizzMergeRequest request = new QuizzMergeRequest(); 41 | request.setGiver(giver); 42 | request.setReceiver(receiver); 43 | save(request); 44 | 45 | } 46 | 47 | public void executeMerge(Long id) { 48 | QuizzMergeRequest request = findById(id); 49 | 50 | QuizzPlayer receivingPlayer = Iterables.getOnlyElement(quizzDao.findByField("name", request.getReceiver()), null); 51 | QuizzPlayer givingPlayer = Iterables.getOnlyElement(quizzDao.findByField("name", request.getGiver()), null); 52 | 53 | delete(request); 54 | receivingPlayer.setPoints(receivingPlayer.getPoints() + givingPlayer.getPoints()); 55 | quizzDao.delete(givingPlayer); 56 | quizzDao.update(receivingPlayer); 57 | 58 | } 59 | 60 | @Override 61 | protected Class getType() { 62 | return QuizzMergeRequest.class; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/dao/QuoteDAO.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.dao; 2 | 3 | import java.util.List; 4 | 5 | import javax.ejb.Stateless; 6 | import javax.persistence.criteria.CriteriaQuery; 7 | import javax.persistence.criteria.Path; 8 | import javax.persistence.criteria.Predicate; 9 | import javax.persistence.criteria.Root; 10 | 11 | import be.hehehe.geekbot.persistence.model.Quote; 12 | 13 | import com.google.common.collect.Iterables; 14 | import com.google.common.collect.Lists; 15 | 16 | @Stateless 17 | public class QuoteDAO extends GenericDAO { 18 | 19 | @Override 20 | public void save(Quote object) { 21 | object.setNumber((int) getCount() + 1); 22 | super.save(object); 23 | } 24 | 25 | public List findByKeywords(List keywords) { 26 | CriteriaQuery query = builder.createQuery(Quote.class); 27 | Root root = query.from(Quote.class); 28 | Path value = root.get("quote"); 29 | 30 | List predicates = Lists.newArrayList(); 31 | for (String keyword : keywords) { 32 | Predicate p = builder.like(builder.lower(value), "%" + keyword.toLowerCase() + "%"); 33 | predicates.add(p); 34 | } 35 | query.where(predicates.toArray(new Predicate[] {})); 36 | return em.createQuery(query).getResultList(); 37 | 38 | } 39 | 40 | @Override 41 | public void delete(Quote object) { 42 | super.delete(object); 43 | int i = 1; 44 | List quotes = findAll(); 45 | for (Quote quote : quotes) { 46 | quote.setNumber(i); 47 | i++; 48 | } 49 | update(quotes.toArray(new Quote[0])); 50 | } 51 | 52 | public Quote findByNumber(int number) { 53 | return Iterables.getOnlyElement(findByField("number", number), null); 54 | } 55 | 56 | @Override 57 | protected Class getType() { 58 | return Quote.class; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/dao/RSSFeedDAO.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.dao; 2 | 3 | import javax.ejb.Stateless; 4 | 5 | import be.hehehe.geekbot.persistence.model.RSSFeed; 6 | 7 | import com.google.common.collect.Iterables; 8 | 9 | @Stateless 10 | public class RSSFeedDAO extends GenericDAO { 11 | public RSSFeed findByGUID(String guid) { 12 | return Iterables.getOnlyElement(findByField("guid", guid), null); 13 | } 14 | 15 | @Override 16 | protected Class getType() { 17 | return RSSFeed.class; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/dao/SkanditeDAO.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.dao; 2 | 3 | import javax.ejb.Stateless; 4 | import javax.persistence.criteria.CriteriaQuery; 5 | import javax.persistence.criteria.Predicate; 6 | import javax.persistence.criteria.Root; 7 | 8 | import be.hehehe.geekbot.persistence.model.Skandite; 9 | 10 | import com.google.common.collect.Iterables; 11 | 12 | @Stateless 13 | public class SkanditeDAO extends GenericDAO { 14 | 15 | public Skandite findByURL(String url) { 16 | return Iterables.getOnlyElement(findByField("url", url), null); 17 | } 18 | 19 | public Skandite findByHashAndByteCount(String hash, Long byteCount) { 20 | 21 | CriteriaQuery query = builder.createQuery(Skandite.class); 22 | Root root = query.from(Skandite.class); 23 | Predicate condition1 = builder.equal(root.get("hash"), hash); 24 | Predicate condition2 = builder.equal(root.get("byteCount"), byteCount); 25 | query.where(builder.and(condition1, condition2)); 26 | return Iterables.getOnlyElement(em.createQuery(query).getResultList(), null); 27 | } 28 | 29 | @Override 30 | protected Class getType() { 31 | return Skandite.class; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/model/Connerie.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.model; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | 9 | @Entity 10 | public class Connerie { 11 | 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.AUTO) 14 | private Long id; 15 | @Column(length = 512) 16 | private String value; 17 | @Column(length = 32) 18 | private String author; 19 | 20 | public Connerie() { 21 | 22 | } 23 | 24 | public Connerie(String author, String value) { 25 | super(); 26 | this.author = author; 27 | this.value = value; 28 | } 29 | 30 | public String getValue() { 31 | return value; 32 | } 33 | 34 | public void setValue(String value) { 35 | this.value = value; 36 | } 37 | 38 | public Long getId() { 39 | return id; 40 | } 41 | 42 | public void setId(Long id) { 43 | this.id = id; 44 | } 45 | 46 | public String getAuthor() { 47 | return author; 48 | } 49 | 50 | public void setAuthor(String author) { 51 | this.author = author; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/model/QuizzMergeException.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.model; 2 | 3 | @SuppressWarnings("serial") 4 | public class QuizzMergeException extends Exception { 5 | 6 | public QuizzMergeException() { 7 | super(); 8 | } 9 | 10 | public QuizzMergeException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/model/QuizzMergeRequest.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | 8 | @Entity 9 | public class QuizzMergeRequest { 10 | 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.AUTO) 13 | private Long id; 14 | 15 | private String giver; 16 | private String receiver; 17 | 18 | public Long getId() { 19 | return id; 20 | } 21 | 22 | public void setId(Long id) { 23 | this.id = id; 24 | } 25 | 26 | public String getGiver() { 27 | return giver; 28 | } 29 | 30 | public void setGiver(String giver) { 31 | this.giver = giver; 32 | } 33 | 34 | public String getReceiver() { 35 | return receiver; 36 | } 37 | 38 | public void setReceiver(String receiver) { 39 | this.receiver = receiver; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/model/QuizzPlayer.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | 8 | @Entity 9 | public class QuizzPlayer { 10 | 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.AUTO) 13 | private Long id; 14 | 15 | private String name; 16 | 17 | private long points; 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | public long getPoints() { 36 | return points; 37 | } 38 | 39 | public void setPoints(long points) { 40 | this.points = points; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/model/Quote.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.model; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | 9 | @Entity 10 | public class Quote { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.AUTO) 13 | private Long id; 14 | @Column(length = 512) 15 | private String quote; 16 | private Integer number; 17 | 18 | public Quote() { 19 | } 20 | 21 | public Quote(String quote) { 22 | this.quote = quote; 23 | } 24 | 25 | public Long getId() { 26 | return id; 27 | } 28 | 29 | public void setId(Long id) { 30 | this.id = id; 31 | } 32 | 33 | public String getQuote() { 34 | return quote; 35 | } 36 | 37 | public void setQuote(String quote) { 38 | this.quote = quote; 39 | } 40 | 41 | public Integer getNumber() { 42 | return number; 43 | } 44 | 45 | public void setNumber(Integer number) { 46 | this.number = number; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/model/RSSFeed.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | 8 | @Entity 9 | public class RSSFeed { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.AUTO) 12 | private Long id; 13 | private String guid; 14 | 15 | public Long getId() { 16 | return id; 17 | } 18 | 19 | public void setId(Long id) { 20 | this.id = id; 21 | } 22 | 23 | public String getGuid() { 24 | return guid; 25 | } 26 | 27 | public void setGuid(String guid) { 28 | this.guid = guid; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/persistence/model/Skandite.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.persistence.model; 2 | 3 | import java.util.Date; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.Temporal; 11 | import javax.persistence.TemporalType; 12 | 13 | @Entity 14 | public class Skandite { 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.AUTO) 17 | private Long id; 18 | @Column(length = 512) 19 | private String url; 20 | private String author; 21 | private Long count; 22 | @Temporal(TemporalType.TIMESTAMP) 23 | private Date postedDate; 24 | private String hash; 25 | private Long byteCount; 26 | 27 | public Long getId() { 28 | return id; 29 | } 30 | 31 | public void setId(Long id) { 32 | this.id = id; 33 | } 34 | 35 | public String getUrl() { 36 | return url; 37 | } 38 | 39 | public void setUrl(String url) { 40 | this.url = url; 41 | } 42 | 43 | public String getAuthor() { 44 | return author; 45 | } 46 | 47 | public void setAuthor(String author) { 48 | this.author = author; 49 | } 50 | 51 | public Long getCount() { 52 | return count; 53 | } 54 | 55 | public void setCount(Long count) { 56 | this.count = count; 57 | } 58 | 59 | public Date getPostedDate() { 60 | return postedDate; 61 | } 62 | 63 | public void setPostedDate(Date postedDate) { 64 | this.postedDate = postedDate; 65 | } 66 | 67 | public String getHash() { 68 | return hash; 69 | } 70 | 71 | public void setHash(String hash) { 72 | this.hash = hash; 73 | } 74 | 75 | public Long getByteCount() { 76 | return byteCount; 77 | } 78 | 79 | public void setByteCount(Long byteCount) { 80 | this.byteCount = byteCount; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/utils/BotUtilsService.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStreamWriter; 6 | import java.io.StringReader; 7 | import java.net.URL; 8 | import java.net.URLConnection; 9 | import java.net.URLEncoder; 10 | import java.nio.ByteBuffer; 11 | import java.nio.CharBuffer; 12 | import java.nio.charset.Charset; 13 | import java.text.Normalizer; 14 | import java.util.Map; 15 | 16 | import javax.enterprise.context.ApplicationScoped; 17 | import javax.inject.Inject; 18 | 19 | import org.apache.commons.codec.digest.DigestUtils; 20 | import org.apache.commons.io.IOUtils; 21 | import org.apache.commons.lang3.StringUtils; 22 | import org.jdom2.Document; 23 | import org.jdom2.JDOMException; 24 | import org.jdom2.input.SAXBuilder; 25 | import org.json.JSONObject; 26 | 27 | import com.google.common.collect.Maps; 28 | 29 | import lombok.extern.jbosslog.JBossLog; 30 | 31 | @ApplicationScoped 32 | @JBossLog 33 | public class BotUtilsService { 34 | 35 | @Inject 36 | BundleService bundleService; 37 | 38 | /** 39 | * Get the content of the specified URL 40 | * 41 | * @param urlString 42 | * @return the content of the url, null if unable to fetch. 43 | */ 44 | public String getContent(String urlString) { 45 | return getContent(urlString, "UTF-8", null); 46 | } 47 | 48 | /** 49 | * Get the content of the specified URL 50 | * 51 | * @param urlString 52 | * @param characterEncoding 53 | * @return the content of the url, null if unable to fetch. 54 | */ 55 | public String getContent(String urlString, String characterEncoding) { 56 | return getContent(urlString, characterEncoding, null); 57 | } 58 | 59 | /** 60 | * Get the content of the specified URL only if the MIME type of the target starts with mimeTypePrefix or if mimeTypePrefix is null. 61 | * 62 | * @param urlString 63 | * @param mimeTypePrefix 64 | * @return the content of the url, null if unable to fetch. 65 | */ 66 | public String getContent(String urlString, String characterEncoding, String mimeTypePrefix) { 67 | String result = null; 68 | InputStream is = null; 69 | try { 70 | URL url = new URL(urlString); 71 | URLConnection con = url.openConnection(); 72 | con.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; H010818)"); 73 | con.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); 74 | con.setConnectTimeout(30000); 75 | con.setReadTimeout(30000); 76 | String contentType = con.getContentType(); 77 | if (mimeTypePrefix == null || contentType.startsWith(mimeTypePrefix)) { 78 | is = con.getInputStream(); 79 | result = IOUtils.toString(is, characterEncoding); 80 | } 81 | } catch (Exception e) { 82 | log.error(e.getMessage(), e); 83 | } finally { 84 | IOUtils.closeQuietly(is); 85 | } 86 | return result; 87 | } 88 | 89 | /** 90 | * Shortens the specified url if possible, returning the original url if not. 91 | * 92 | * @param url 93 | * @return the shorten url, the original url if unable to do so. 94 | */ 95 | public String bitly(String url) { 96 | String result = url; 97 | InputStream is = null; 98 | String login = bundleService.getBitlyLogin(); 99 | String apiKey = bundleService.getBitlyApiKey(); 100 | if (StringUtils.isNotBlank(login) && StringUtils.isNotBlank(apiKey)) { 101 | try { 102 | URL urlObject = new URL("http://api.bit.ly/shorten?version=2.0.1&longUrl=" + url + "&login=" + login + "&apiKey=" + apiKey); 103 | result = IOUtils.toString(urlObject.openStream()); 104 | JSONObject json = new JSONObject(result); 105 | result = json.getJSONObject("results").getJSONObject(url).getString("shortUrl"); 106 | } catch (Exception e) { 107 | log.error(e.getMessage(), e); 108 | } finally { 109 | IOUtils.closeQuietly(is); 110 | } 111 | } 112 | return result; 113 | } 114 | 115 | /** 116 | * Mirrors image on imgur 117 | */ 118 | public String mirrorImage(String urlString) { 119 | String result = null; 120 | OutputStreamWriter wr = null; 121 | InputStream is = null; 122 | try { 123 | String clientId = bundleService.getImgurClientId(); 124 | if (StringUtils.isBlank(clientId)) { 125 | return "Imgur clientId not set."; 126 | } 127 | 128 | URL apiUrl = new URL("https://api.imgur.com/3/image"); 129 | 130 | String data = URLEncoder.encode("image", "UTF-8") + "=" + URLEncoder.encode(urlString, "UTF-8"); 131 | data += "&" + URLEncoder.encode("type", "UTF-8") + "=" + URLEncoder.encode("URL", "UTF-8"); 132 | 133 | URLConnection apiCon = apiUrl.openConnection(); 134 | apiCon.setDoOutput(true); 135 | apiCon.setDoInput(true); 136 | apiCon.setRequestProperty("Authorization", "Client-ID " + clientId); 137 | wr = new OutputStreamWriter(apiCon.getOutputStream()); 138 | wr.write(data); 139 | wr.flush(); 140 | is = apiCon.getInputStream(); 141 | String json = IOUtils.toString(is); 142 | JSONObject root = new JSONObject(json); 143 | result = root.getJSONObject("data").getString("link"); 144 | } catch (Exception e) { 145 | log.error(e.getMessage(), e); 146 | } finally { 147 | IOUtils.closeQuietly(wr); 148 | IOUtils.closeQuietly(is); 149 | } 150 | return result; 151 | } 152 | 153 | /** 154 | * Parses an url and return a map of request parameters 155 | * 156 | * @param url 157 | * @return the map of request parameters 158 | */ 159 | public Map getRequestParametersFromURL(String url) { 160 | Map map = Maps.newHashMap(); 161 | int index; 162 | if ((index = url.indexOf("?")) != -1) { 163 | for (String keyvalue : url.substring(index + 1).split("&")) { 164 | String[] split = keyvalue.split("="); 165 | if (split.length == 2) { 166 | map.put(split[0], split[1]); 167 | } 168 | } 169 | } 170 | return map; 171 | } 172 | 173 | /** 174 | * Returns the first url found in the given string, or null if no url is found. 175 | * 176 | * @param message 177 | * @return an url string or null if none found. 178 | */ 179 | public String extractURL(String message) { 180 | String url = null; 181 | if (message == null) { 182 | return null; 183 | } 184 | for (String s : message.split(" ")) { 185 | if (s.contains("http://") || s.contains("https://") || s.contains("www.")) { 186 | if (s.startsWith("www.")) { 187 | s = "http://" + s; 188 | } 189 | if (s.endsWith("/")) { 190 | s = s.substring(0, s.length() - 1); 191 | } 192 | 193 | if (s.contains("youtube.com")) { 194 | String videoParam = getRequestParametersFromURL(s).get("v"); 195 | if (videoParam != null) { 196 | s = "http://www.youtube.com/watch?v=" + videoParam; 197 | } 198 | } else if (s.contains("youtu.be")) { 199 | String videoParam = extractIDFromYoutuDotBeURL(s); 200 | s = "http://www.youtube.com/watch?v=" + videoParam; 201 | } else { 202 | Map map = getRequestParametersFromURL(s); 203 | URLBuilder urlBuilder = new URLBuilder(s.split("[?]")[0]); 204 | for (Map.Entry e : map.entrySet()) { 205 | urlBuilder.addParam(e.getKey(), e.getValue()); 206 | } 207 | s = urlBuilder.build(); 208 | } 209 | url = s; 210 | break; 211 | } 212 | } 213 | return url; 214 | } 215 | 216 | public String extractIDFromYoutuDotBeURL(String url) { 217 | String videoParam = url.substring(url.lastIndexOf("/") + 1); 218 | int indexOfSlash = videoParam.indexOf("?"); 219 | if (indexOfSlash > -1) { 220 | videoParam = videoParam.substring(0, indexOfSlash); 221 | } 222 | return videoParam; 223 | } 224 | 225 | public HashAndByteCount calculateHashAndByteCount(String urlString) { 226 | HashAndByteCount hashAndByteCount = new HashAndByteCount(); 227 | 228 | String content = getContent(urlString, "UTF-8", "image/"); 229 | if (content != null) { 230 | byte[] bytes = content.getBytes(); 231 | 232 | hashAndByteCount.setHash(DigestUtils.md5Hex(bytes)); 233 | hashAndByteCount.setByteCount(Long.valueOf(bytes.length)); 234 | } 235 | return hashAndByteCount; 236 | } 237 | 238 | public Document parseXML(String xml) throws JDOMException, IOException { 239 | SAXBuilder builder = new SAXBuilder(); 240 | Document doc = builder.build(new StringReader(xml)); 241 | return doc; 242 | } 243 | 244 | public String stripAccents(String source) { 245 | source = Normalizer.normalize(source, Normalizer.Form.NFD); 246 | source = source.replaceAll("[\u0300-\u036F]", ""); 247 | return source; 248 | } 249 | 250 | public String utf8ToIso88591(String input) { 251 | if (input.contains("Ã") || input.contains("©")) { 252 | Charset utf8charset = Charset.forName("UTF-8"); 253 | Charset iso88591charset = Charset.forName("ISO-8859-1"); 254 | 255 | ByteBuffer inputBuffer = ByteBuffer.wrap(input.getBytes()); 256 | CharBuffer data = utf8charset.decode(inputBuffer); 257 | 258 | ByteBuffer outputBuffer = iso88591charset.encode(data); 259 | byte[] outputData = outputBuffer.array(); 260 | return new String(outputData); 261 | } else { 262 | return input; 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/utils/BundleService.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.utils; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.InputStream; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Properties; 8 | import java.util.regex.Pattern; 9 | 10 | import javax.annotation.PostConstruct; 11 | import javax.enterprise.context.ApplicationScoped; 12 | 13 | import org.apache.commons.io.IOUtils; 14 | 15 | import lombok.extern.jbosslog.JBossLog; 16 | 17 | @ApplicationScoped 18 | @JBossLog 19 | public class BundleService { 20 | 21 | private Properties props; 22 | 23 | @PostConstruct 24 | public void init() { 25 | props = new Properties(); 26 | InputStream is = null; 27 | try { 28 | log.info("loading config file"); 29 | props.load(new FileInputStream("config.properties")); 30 | } catch (Exception e) { 31 | log.fatal("Could not load config file", e); 32 | } finally { 33 | IOUtils.closeQuietly(is); 34 | } 35 | } 36 | 37 | public String getAdminPassword() { 38 | return getValue("admin.password"); 39 | } 40 | 41 | public String getBotName() { 42 | return getValue("botname"); 43 | } 44 | 45 | public List getChannels() { 46 | return Arrays.asList(getValue("channels").split(Pattern.quote(","))); 47 | } 48 | 49 | public String getDiscordToken() { 50 | return getValue("discord.token"); 51 | } 52 | 53 | public String getBitlyLogin() { 54 | return getValue("bitly.login"); 55 | } 56 | 57 | public String getBitlyApiKey() { 58 | return getValue("bitly.apikey"); 59 | } 60 | 61 | public String getImgurClientId() { 62 | return getValue("imgur.clientId"); 63 | } 64 | 65 | public String getVDMApiKey() { 66 | return getValue("vdm.apikey"); 67 | } 68 | 69 | public String getMemeGeneratorLogin() { 70 | return getValue("meme.login"); 71 | } 72 | 73 | public String getMemeGeneratorPassword() { 74 | return getValue("meme.password"); 75 | } 76 | 77 | public String getWebServerRootPath() { 78 | String host = getValue("webserver.hostname"); 79 | if (host.endsWith("/")) { 80 | host = host.substring(0, host.length() - 1); 81 | } 82 | return host; 83 | } 84 | 85 | public int getWebServerPort() { 86 | return Integer.parseInt(getValue("webserver.port")); 87 | } 88 | 89 | public String getTwitterConsumerKey() { 90 | return getValue("twitter.consumerKey"); 91 | } 92 | 93 | public String getTwitterConsumerSecret() { 94 | return getValue("twitter.consumerSecret"); 95 | } 96 | 97 | public String getTwitterToken() { 98 | return getValue("twitter.token"); 99 | } 100 | 101 | public String getTwitterTokenSecret() { 102 | return getValue("twitter.tokenSecret"); 103 | } 104 | 105 | public String getGoogleKey() { 106 | return getValue("google.key"); 107 | } 108 | 109 | public String getGoogleCseId() { 110 | return getValue("google.cseId"); 111 | } 112 | 113 | public boolean isTest() { 114 | return "true".equals(getValue("test")); 115 | } 116 | 117 | public String getDiscordBotName() { 118 | return getValue("discord.botname"); 119 | } 120 | 121 | public String getValue(String key) { 122 | return props.getProperty(key, null); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/utils/DiscordUtils.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.utils; 2 | 3 | public class DiscordUtils { 4 | 5 | public static String bold(String s) { 6 | return "**" + s + "**"; 7 | } 8 | 9 | public static String linkWithoutPreview(String linkUrl) { 10 | return "<" + linkUrl + ">"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/utils/HashAndByteCount.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.utils; 2 | 3 | public class HashAndByteCount { 4 | private String hash; 5 | private Long byteCount; 6 | 7 | public String getHash() { 8 | return hash; 9 | } 10 | 11 | public void setHash(String hash) { 12 | this.hash = hash; 13 | } 14 | 15 | public Long getByteCount() { 16 | return byteCount; 17 | } 18 | 19 | public void setByteCount(Long byteCount) { 20 | this.byteCount = byteCount; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/utils/ResourcesFactory.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.utils; 2 | 3 | import java.util.Random; 4 | 5 | import javax.enterprise.context.ApplicationScoped; 6 | import javax.enterprise.inject.Produces; 7 | import javax.inject.Inject; 8 | 9 | import twitter4j.Twitter; 10 | import twitter4j.TwitterFactory; 11 | import twitter4j.auth.AccessToken; 12 | 13 | @ApplicationScoped 14 | public class ResourcesFactory { 15 | 16 | @Inject 17 | BundleService bundle; 18 | 19 | private Random random = new Random(); 20 | private Twitter twitter; 21 | 22 | @Produces 23 | public Random getRandom() { 24 | return random; 25 | } 26 | 27 | @Produces 28 | public synchronized Twitter getTwitter() { 29 | if (twitter == null) { 30 | twitter = TwitterFactory.getSingleton(); 31 | twitter.setOAuthConsumer(bundle.getTwitterConsumerKey(), bundle.getTwitterConsumerSecret()); 32 | twitter.setOAuthAccessToken(new AccessToken(bundle.getTwitterToken(), bundle.getTwitterTokenSecret())); 33 | } 34 | return twitter; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/utils/StartupBean.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.utils; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.ejb.Singleton; 5 | import javax.ejb.Startup; 6 | import javax.inject.Inject; 7 | 8 | import be.hehehe.geekbot.bot.GeekBot; 9 | import lombok.extern.jbosslog.JBossLog; 10 | 11 | @Singleton 12 | @Startup 13 | @JBossLog 14 | public class StartupBean { 15 | 16 | @Inject 17 | GeekBot bot; 18 | 19 | public StartupBean() { 20 | log.info("Bot initializing"); 21 | } 22 | 23 | @PostConstruct 24 | private void init() { 25 | log.info("Bot initialized"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/utils/URLBuilder.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | public class URLBuilder { 9 | private String baseURL; 10 | private List params; 11 | 12 | public URLBuilder(String baseURL) { 13 | this.baseURL = baseURL; 14 | this.params = new ArrayList(); 15 | } 16 | 17 | public URLBuilder addParam(String name, String value) { 18 | if (!StringUtils.isEmpty(name) && !StringUtils.isEmpty(value)) { 19 | params.add(name + "=" + value); 20 | } 21 | return this; 22 | } 23 | 24 | public String build() { 25 | String result = null; 26 | String paramString = StringUtils.join(params, "&"); 27 | if (baseURL == null) { 28 | result = paramString; 29 | } else if (StringUtils.isEmpty(paramString)) { 30 | return baseURL; 31 | } else { 32 | int position = baseURL.indexOf("?"); 33 | if (position == -1) { 34 | result = baseURL + "?" + paramString; 35 | } else if (position == baseURL.length() - 1) { 36 | result = baseURL + paramString; 37 | } else { 38 | result = baseURL + "&" + paramString; 39 | } 40 | } 41 | return result; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/HelpPage.css: -------------------------------------------------------------------------------- 1 | .class ul { 2 | margin-left: 20px; 3 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/HelpPage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 |
11 |

12 |
    13 |
  • 14 |

    15 | 16 |

    17 |

    18 | 19 |

    20 |
  • 21 |
22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/HelpPage.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web; 2 | 3 | import java.io.Serializable; 4 | import java.lang.reflect.Method; 5 | import java.util.Collections; 6 | import java.util.Comparator; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import javax.inject.Inject; 11 | 12 | import org.apache.wicket.markup.head.IHeaderResponse; 13 | import org.apache.wicket.markup.html.basic.Label; 14 | import org.apache.wicket.markup.html.list.ListItem; 15 | import org.apache.wicket.markup.html.list.ListView; 16 | import org.apache.wicket.model.LoadableDetachableModel; 17 | 18 | import be.hehehe.geekbot.annotations.Help; 19 | import be.hehehe.geekbot.annotations.Trigger; 20 | import be.hehehe.geekbot.annotations.TriggerType; 21 | import be.hehehe.geekbot.annotations.Triggers; 22 | import be.hehehe.geekbot.web.utils.WicketUtils; 23 | 24 | import com.google.common.collect.Lists; 25 | import com.google.common.collect.Maps; 26 | 27 | @SuppressWarnings("serial") 28 | public class HelpPage extends TemplatePage { 29 | 30 | @Inject 31 | @Triggers 32 | List triggers; 33 | 34 | public HelpPage() { 35 | ListView view = new ListView("class", new MappedTriggerModel()) { 36 | 37 | @Override 38 | protected void populateItem(ListItem item) { 39 | TriggerModel model = item.getModelObject(); 40 | item.add(new Label("name", model.getKlass().getSimpleName())); 41 | item.add(new ListView("trigger", model.getTriggers()) { 42 | 43 | @Override 44 | protected void populateItem(ListItem item) { 45 | Method method = item.getModelObject(); 46 | Trigger trigger = method.getAnnotation(Trigger.class); 47 | Help help = method.getAnnotation(Help.class); 48 | item.add(new Label("name", trigger.value())); 49 | item.add(new Label("type", trigger.type() == TriggerType.STARTSWITH ? "starts with" : "exact match")); 50 | item.add(new Label("desc", help == null ? "No description." : help.value())); 51 | } 52 | }); 53 | } 54 | }; 55 | add(view); 56 | } 57 | 58 | @Override 59 | protected String getTitle() { 60 | return "Help"; 61 | } 62 | 63 | @Override 64 | public void renderHead(IHeaderResponse response) { 65 | super.renderHead(response); 66 | WicketUtils.loadCSS(response, HelpPage.class); 67 | } 68 | 69 | private class MappedTriggerModel extends LoadableDetachableModel> { 70 | 71 | @Override 72 | protected List load() { 73 | Map, List> map = Maps.newHashMap(); 74 | for (Method method : triggers) { 75 | Trigger trigger = method.getAnnotation(Trigger.class); 76 | if (trigger.type() == TriggerType.EXACTMATCH || trigger.type() == TriggerType.STARTSWITH) { 77 | List list = map.get(method.getDeclaringClass()); 78 | if (list == null) { 79 | list = Lists.newArrayList(); 80 | map.put(method.getDeclaringClass(), list); 81 | } 82 | list.add(method); 83 | } 84 | } 85 | 86 | List list = Lists.newArrayList(); 87 | for (Class key : map.keySet()) { 88 | List methods = map.get(key); 89 | Collections.sort(methods, new Comparator() { 90 | @Override 91 | public int compare(Method o1, Method o2) { 92 | return o1.getAnnotation(Trigger.class).value().compareTo(o2.getAnnotation(Trigger.class).value()); 93 | } 94 | }); 95 | list.add(new TriggerModel(key, methods)); 96 | } 97 | 98 | Collections.sort(list, new Comparator() { 99 | 100 | @Override 101 | public int compare(TriggerModel o1, TriggerModel o2) { 102 | return o1.getKlass().getSimpleName().compareTo(o2.getKlass().getSimpleName()); 103 | } 104 | }); 105 | return list; 106 | } 107 | } 108 | 109 | private static class TriggerModel implements Serializable { 110 | private Class klass; 111 | private List triggers; 112 | 113 | public TriggerModel(Class klass, List triggers) { 114 | this.klass = klass; 115 | this.triggers = triggers; 116 | } 117 | 118 | public Class getKlass() { 119 | return klass; 120 | } 121 | 122 | public List getTriggers() { 123 | return triggers; 124 | } 125 | 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/HomePage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 |
7 |

8 | 9 |

10 |

This is the home page. Use the menu on the left to navigate to 11 | the other pages.

12 |

13 | View project on GitHub 15 |

16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/HomePage.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.apache.wicket.markup.html.basic.Label; 6 | 7 | import be.hehehe.geekbot.utils.BundleService; 8 | 9 | @SuppressWarnings("serial") 10 | public class HomePage extends TemplatePage { 11 | 12 | @Inject 13 | BundleService bundleService; 14 | 15 | public HomePage() { 16 | add(new Label("bot-name", bundleService.getBotName())); 17 | } 18 | 19 | @Override 20 | protected String getTitle() { 21 | return "Home"; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/QuizzMergePage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 |
10 |
11 |
12 |
13 | 15 |
16 | 17 |
18 |
19 |
20 | 22 |
23 | 24 |
25 |
26 |

Giving player will be deleted and 27 | receiving player will get his/her points.

28 |
29 | 31 |
32 |
33 |
34 | 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
Receiving PlayerGiving Player
56 |
57 |
58 |
59 |
60 | 61 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/QuizzMergePage.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web; 2 | 3 | import java.util.List; 4 | 5 | import javax.inject.Inject; 6 | 7 | import org.apache.wicket.authroles.authorization.strategies.role.Roles; 8 | import org.apache.wicket.markup.html.basic.Label; 9 | import org.apache.wicket.markup.html.form.Button; 10 | import org.apache.wicket.markup.html.form.DropDownChoice; 11 | import org.apache.wicket.markup.html.form.Form; 12 | import org.apache.wicket.markup.html.form.SubmitLink; 13 | import org.apache.wicket.markup.html.list.ListItem; 14 | import org.apache.wicket.markup.html.list.ListView; 15 | import org.apache.wicket.markup.html.list.PropertyListView; 16 | import org.apache.wicket.markup.html.panel.FeedbackPanel; 17 | import org.apache.wicket.model.IModel; 18 | import org.apache.wicket.model.LoadableDetachableModel; 19 | import org.apache.wicket.model.Model; 20 | 21 | import be.hehehe.geekbot.persistence.dao.QuizzDAO; 22 | import be.hehehe.geekbot.persistence.dao.QuizzMergeDAO; 23 | import be.hehehe.geekbot.persistence.model.QuizzMergeException; 24 | import be.hehehe.geekbot.persistence.model.QuizzMergeRequest; 25 | import be.hehehe.geekbot.persistence.model.QuizzPlayer; 26 | import be.hehehe.geekbot.web.components.ChosenBehavior; 27 | 28 | import com.google.common.base.Function; 29 | import com.google.common.collect.Lists; 30 | 31 | @SuppressWarnings("serial") 32 | public class QuizzMergePage extends TemplatePage { 33 | 34 | @Inject 35 | QuizzDAO quizzDAO; 36 | 37 | @Inject 38 | QuizzMergeDAO mergeDAO; 39 | 40 | public QuizzMergePage() { 41 | add(new SubmitForm("form")); 42 | add(new MergeForm("merge-form")); 43 | } 44 | 45 | private class SubmitForm extends Form> { 46 | 47 | private IModel giver = new Model(); 48 | private IModel receiver = new Model(); 49 | 50 | FeedbackPanel messages = new FeedbackPanel("messages"); 51 | 52 | public SubmitForm(String id) { 53 | super(id); 54 | messages.setVisible(false); 55 | 56 | IModel> model = new LoadableDetachableModel>() { 57 | @Override 58 | protected List load() { 59 | return Lists.transform(quizzDAO.getPlayersOrderByName(), new Function() { 60 | @Override 61 | public String apply(QuizzPlayer input) { 62 | return input.getName(); 63 | } 64 | }); 65 | } 66 | }; 67 | 68 | add(new Button("submit-button")); 69 | DropDownChoice giverChoice = new DropDownChoice("giver", giver, model); 70 | giverChoice.add(new ChosenBehavior()); 71 | add(giverChoice); 72 | 73 | DropDownChoice receiverChoice = new DropDownChoice("receiver", receiver, model); 74 | receiverChoice.add(new ChosenBehavior()); 75 | add(receiverChoice); 76 | 77 | add(messages); 78 | } 79 | 80 | @Override 81 | protected void onSubmit() { 82 | try { 83 | mergeDAO.add(receiver.getObject(), giver.getObject()); 84 | setResponsePage(QuizzMergePage.class); 85 | } catch (QuizzMergeException e) { 86 | messages.setVisible(true); 87 | error(e.getMessage()); 88 | } 89 | } 90 | 91 | } 92 | 93 | private class MergeForm extends Form> { 94 | 95 | public MergeForm(String id) { 96 | super(id); 97 | 98 | IModel> model = new LoadableDetachableModel>() { 99 | @Override 100 | protected List load() { 101 | return mergeDAO.findAll(); 102 | } 103 | }; 104 | 105 | ListView requestsView = new PropertyListView("requests", model) { 106 | @Override 107 | protected void populateItem(ListItem item) { 108 | QuizzMergeRequest request = item.getModelObject(); 109 | final Long requestId = request.getId(); 110 | item.add(new Label("receiving", request.getReceiver())); 111 | item.add(new Label("giving", request.getGiver())); 112 | 113 | SubmitLink accept = new SubmitLink("accept") { 114 | @Override 115 | public void onSubmit() { 116 | mergeDAO.executeMerge(requestId); 117 | setResponsePage(QuizzMergePage.class); 118 | } 119 | }; 120 | 121 | SubmitLink deny = new SubmitLink("deny") { 122 | @Override 123 | public void onSubmit() { 124 | mergeDAO.deleteById(requestId); 125 | setResponsePage(QuizzMergePage.class); 126 | } 127 | }; 128 | 129 | boolean hasRole = getAuthSession().getRoles().contains(Roles.ADMIN); 130 | accept.setVisible(hasRole); 131 | deny.setVisible(hasRole); 132 | 133 | item.add(accept); 134 | item.add(deny); 135 | } 136 | }; 137 | requestsView.setReuseItems(true); 138 | add(requestsView); 139 | } 140 | } 141 | 142 | @Override 143 | protected String getTitle() { 144 | return "Quizz Merge Requests"; 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/QuizzScorePage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
RankPlayer NamePoints
26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/QuizzScorePage.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web; 2 | 3 | import java.util.List; 4 | 5 | import javax.inject.Inject; 6 | 7 | import org.apache.wicket.markup.html.basic.Label; 8 | import org.apache.wicket.markup.html.list.ListItem; 9 | import org.apache.wicket.markup.html.list.ListView; 10 | import org.apache.wicket.markup.html.list.PropertyListView; 11 | import org.apache.wicket.model.IModel; 12 | import org.apache.wicket.model.LoadableDetachableModel; 13 | 14 | import be.hehehe.geekbot.persistence.dao.QuizzDAO; 15 | import be.hehehe.geekbot.persistence.model.QuizzPlayer; 16 | 17 | @SuppressWarnings("serial") 18 | public class QuizzScorePage extends TemplatePage { 19 | 20 | @Inject 21 | QuizzDAO quizzDAO; 22 | 23 | public QuizzScorePage() { 24 | 25 | IModel> model = new LoadableDetachableModel>() { 26 | @Override 27 | protected List load() { 28 | return quizzDAO.getPlayersOrderByPoints(); 29 | } 30 | }; 31 | 32 | ListView playersView = new PropertyListView("players", model) { 33 | @Override 34 | protected void populateItem(ListItem item) { 35 | item.add(new Label("rank", "" + (item.getIndex() + 1))); 36 | item.add(new Label("name")); 37 | item.add(new Label("points")); 38 | } 39 | }; 40 | 41 | add(playersView); 42 | } 43 | 44 | @Override 45 | protected String getTitle() { 46 | return "Quizz Scoreboard"; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/TemplatePage.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 60px; 3 | /* 60px to make the container go all the way to the bottom of the topbar */ 4 | } 5 | 6 | .center { 7 | margin-left: auto; 8 | margin-right: auto; 9 | text-align: center; 10 | } 11 | 12 | #players td { 13 | width: 250px; 14 | } 15 | 16 | .pointer { 17 | cursor: pointer; 18 | } 19 | 20 | .block { 21 | display: block; 22 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/TemplatePage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 23 | 24 |
25 |
26 |
27 | 31 |
32 | 33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 | 41 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/TemplatePage.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.apache.wicket.bootstrap.Bootstrap; 6 | import org.apache.wicket.markup.head.CssHeaderItem; 7 | import org.apache.wicket.markup.head.IHeaderResponse; 8 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 9 | import org.apache.wicket.markup.html.WebPage; 10 | import org.apache.wicket.markup.html.basic.Label; 11 | import org.apache.wicket.markup.repeater.RepeatingView; 12 | import org.apache.wicket.request.resource.CssResourceReference; 13 | import org.apache.wicket.request.resource.JavaScriptResourceReference; 14 | 15 | import com.google.common.collect.LinkedListMultimap; 16 | import com.google.common.collect.Multimap; 17 | 18 | import be.hehehe.geekbot.utils.BundleService; 19 | import be.hehehe.geekbot.web.auth.LoggedInButtonPanel; 20 | import be.hehehe.geekbot.web.auth.LoggedOutButtonPanel; 21 | import be.hehehe.geekbot.web.auth.WicketSession; 22 | import be.hehehe.geekbot.web.nav.NavigationHeader; 23 | 24 | @SuppressWarnings("serial") 25 | public abstract class TemplatePage extends WebPage { 26 | 27 | @Inject 28 | BundleService bundleService; 29 | 30 | public TemplatePage() { 31 | 32 | add(new Label("title", getTitle())); 33 | add(new Label("project-name", bundleService.getBotName())); 34 | 35 | String buttonId = "topright-button"; 36 | if (getAuthSession().isSignedIn()) { 37 | add(new LoggedInButtonPanel(buttonId)); 38 | } else { 39 | add(new LoggedOutButtonPanel(buttonId)); 40 | } 41 | 42 | addNavigationMenu(); 43 | 44 | } 45 | 46 | private void addNavigationMenu() { 47 | Multimap pages = LinkedListMultimap.create(); 48 | pages.put("Home", new PageModel("Home Page", HomePage.class)); 49 | pages.put("Quizz", new PageModel("Scoreboard", QuizzScorePage.class)); 50 | pages.put("Quizz", new PageModel("Merge Requests", QuizzMergePage.class)); 51 | pages.put("Help", new PageModel("Triggers", HelpPage.class)); 52 | 53 | RepeatingView repeatingView = new RepeatingView("nav-headers"); 54 | 55 | for (String category : pages.keySet()) { 56 | repeatingView.add(new NavigationHeader(repeatingView.newChildId(), category, pages.get(category))); 57 | } 58 | add(repeatingView); 59 | 60 | } 61 | 62 | @Override 63 | public void renderHead(IHeaderResponse response) { 64 | super.renderHead(response); 65 | response.render(JavaScriptHeaderItem.forReference(Bootstrap.responsive())); 66 | response.render(JavaScriptHeaderItem 67 | .forReference(new JavaScriptResourceReference(TemplatePage.class, TemplatePage.class.getSimpleName() + ".js"))); 68 | response.render( 69 | CssHeaderItem.forReference(new CssResourceReference(TemplatePage.class, TemplatePage.class.getSimpleName() + ".css"))); 70 | } 71 | 72 | protected abstract String getTitle(); 73 | 74 | public static class PageModel { 75 | private String name; 76 | private Class pageClass; 77 | 78 | public PageModel(String name, Class pageClass) { 79 | super(); 80 | this.name = name; 81 | this.pageClass = pageClass; 82 | } 83 | 84 | public Class getPageClass() { 85 | return pageClass; 86 | } 87 | 88 | public String getName() { 89 | return name; 90 | } 91 | 92 | } 93 | 94 | public WicketSession getAuthSession() { 95 | return (WicketSession) super.getSession(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/TemplatePage.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Athou/GeekBot/0c9f05db02537cdc2ea0ddbdc5bd2d34b7b71c9d/src/main/java/be/hehehe/geekbot/web/TemplatePage.js -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/WicketApplication.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web; 2 | 3 | import javax.enterprise.inject.spi.BeanManager; 4 | import javax.naming.InitialContext; 5 | import javax.naming.NamingException; 6 | 7 | import org.apache.wicket.Application; 8 | import org.apache.wicket.Page; 9 | import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; 10 | import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication; 11 | import org.apache.wicket.cdi.CdiConfiguration; 12 | import org.apache.wicket.cdi.ConversationPropagation; 13 | import org.apache.wicket.markup.html.WebPage; 14 | 15 | import be.hehehe.geekbot.web.auth.LoginPage; 16 | import be.hehehe.geekbot.web.auth.LogoutPage; 17 | import be.hehehe.geekbot.web.auth.WicketSession; 18 | 19 | public class WicketApplication extends AuthenticatedWebApplication { 20 | 21 | @Override 22 | protected void init() { 23 | super.init(); 24 | setupCDI(); 25 | 26 | mountPage("login", LoginPage.class); 27 | mountPage("logout", LogoutPage.class); 28 | 29 | mountPage("help", HelpPage.class); 30 | 31 | mountPage("quizz", QuizzScorePage.class); 32 | mountPage("quizzmerge", QuizzMergePage.class); 33 | 34 | getMarkupSettings().setStripWicketTags(true); 35 | } 36 | 37 | protected void setupCDI() { 38 | try { 39 | BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager"); 40 | new CdiConfiguration(beanManager).setPropagation(ConversationPropagation.NONE).configure(this); 41 | } catch (NamingException e) { 42 | throw new IllegalStateException("Unable to obtain CDI BeanManager", e); 43 | } 44 | } 45 | 46 | @Override 47 | public Class getHomePage() { 48 | return HomePage.class; 49 | } 50 | 51 | @Override 52 | protected Class getSignInPageClass() { 53 | return LoginPage.class; 54 | } 55 | 56 | @Override 57 | protected Class getWebSessionClass() { 58 | return WicketSession.class; 59 | } 60 | 61 | public static WicketApplication get() { 62 | return (WicketApplication) Application.get(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoggedInButtonPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 |
7 | 10 | 11 | 14 |
15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoggedInButtonPanel.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.auth; 2 | 3 | import org.apache.wicket.markup.html.basic.Label; 4 | import org.apache.wicket.markup.html.link.BookmarkablePageLink; 5 | import org.apache.wicket.markup.html.panel.Panel; 6 | 7 | @SuppressWarnings("serial") 8 | public class LoggedInButtonPanel extends Panel { 9 | 10 | public LoggedInButtonPanel(String id) { 11 | super(id); 12 | add(new Label("username", (String) getSession().getAttribute("name"))); 13 | add(new BookmarkablePageLink("signout", LogoutPage.class)); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoggedOutButtonPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoggedOutButtonPanel.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.auth; 2 | 3 | import org.apache.wicket.markup.html.link.BookmarkablePageLink; 4 | import org.apache.wicket.markup.html.panel.Panel; 5 | 6 | @SuppressWarnings("serial") 7 | public class LoggedOutButtonPanel extends Panel { 8 | 9 | public LoggedOutButtonPanel(String id) { 10 | super(id); 11 | add(new BookmarkablePageLink("login", LoginPage.class)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoginPage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 |
10 | 11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoginPage.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.auth; 2 | 3 | import be.hehehe.geekbot.web.TemplatePage; 4 | 5 | @SuppressWarnings("serial") 6 | public class LoginPage extends TemplatePage { 7 | 8 | public LoginPage() { 9 | add(new LoginPanel("login")); 10 | } 11 | 12 | @Override 13 | protected String getTitle() { 14 | return "Login"; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoginPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 | 13 |
14 |
15 |
16 | 17 |
18 | 19 |
20 |
21 |

22 | Remember me 23 |

24 |
25 | 26 |
27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LoginPanel.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.auth; 2 | 3 | import org.apache.wicket.authroles.authentication.panel.SignInPanel; 4 | 5 | @SuppressWarnings("serial") 6 | public class LoginPanel extends SignInPanel { 7 | 8 | public LoginPanel(String id) { 9 | super(id); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/LogoutPage.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.auth; 2 | 3 | import be.hehehe.geekbot.web.TemplatePage; 4 | 5 | @SuppressWarnings("serial") 6 | public class LogoutPage extends TemplatePage { 7 | 8 | public LogoutPage() { 9 | getSession().invalidate(); 10 | setResponsePage(getApplication().getHomePage()); 11 | } 12 | 13 | @Override 14 | protected String getTitle() { 15 | return "Logout"; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/auth/WicketSession.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.auth; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; 7 | import org.apache.wicket.authroles.authorization.strategies.role.Roles; 8 | import org.apache.wicket.request.Request; 9 | 10 | import be.hehehe.geekbot.utils.BundleService; 11 | 12 | @SuppressWarnings("serial") 13 | public class WicketSession extends AuthenticatedWebSession { 14 | 15 | @Inject 16 | BundleService bundleService; 17 | 18 | public WicketSession(Request request) { 19 | super(request); 20 | } 21 | 22 | @Override 23 | public Roles getRoles() { 24 | Roles roles = new Roles(); 25 | if (isSignedIn()) { 26 | roles.add(Roles.ADMIN); 27 | } 28 | return roles; 29 | } 30 | 31 | @Override 32 | public boolean authenticate(String username, String password) { 33 | String adminPassword = bundleService.getAdminPassword(); 34 | if (StringUtils.equals(password, adminPassword)) { 35 | setAttribute("name", username); 36 | return true; 37 | } else { 38 | return false; 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/components/BootstrapFeedbackPanel.css: -------------------------------------------------------------------------------- 1 | .bs-fb ul { 2 | margin-bottom: 0px; 3 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/components/BootstrapFeedbackPanel.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.components; 2 | 3 | import org.apache.wicket.behavior.AttributeAppender; 4 | import org.apache.wicket.bootstrap.Bootstrap; 5 | import org.apache.wicket.feedback.IFeedbackMessageFilter; 6 | import org.apache.wicket.markup.head.IHeaderResponse; 7 | import org.apache.wicket.markup.html.panel.FeedbackPanel; 8 | import org.apache.wicket.model.AbstractReadOnlyModel; 9 | 10 | import be.hehehe.geekbot.web.utils.WicketUtils; 11 | 12 | public class BootstrapFeedbackPanel extends FeedbackPanel { 13 | 14 | private static final long serialVersionUID = -4454548249075569147L; 15 | 16 | public BootstrapFeedbackPanel(String id) { 17 | super(id); 18 | init(); 19 | } 20 | 21 | public BootstrapFeedbackPanel(String id, IFeedbackMessageFilter filter) { 22 | super(id, filter); 23 | init(); 24 | } 25 | 26 | private void init() { 27 | add(new AttributeAppender("class", new AbstractReadOnlyModel() { 28 | private static final long serialVersionUID = -2728496420265562466L; 29 | 30 | @Override 31 | public String getObject() { 32 | StringBuilder sb = new StringBuilder(); 33 | if (anyMessage()) { 34 | sb.append(" bs-fb alert"); 35 | } 36 | if (anyErrorMessage()) { 37 | sb.append(" alert-error"); 38 | } else { 39 | sb.append(" alert-success"); 40 | } 41 | return sb.toString(); 42 | } 43 | })); 44 | 45 | get("feedbackul").add(new AttributeAppender("class", " unstyled")); 46 | } 47 | 48 | @Override 49 | public void renderHead(IHeaderResponse response) { 50 | super.renderHead(response); 51 | Bootstrap.renderHeadResponsive(response); 52 | WicketUtils.loadCSS(response, BootstrapFeedbackPanel.class); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/components/ChosenBehavior.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.components; 2 | 3 | import org.apache.wicket.Component; 4 | import org.apache.wicket.behavior.Behavior; 5 | import org.apache.wicket.markup.head.IHeaderResponse; 6 | import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 7 | 8 | import be.hehehe.geekbot.web.components.references.ChosenReference; 9 | 10 | public class ChosenBehavior extends Behavior { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | @Override 15 | public void renderHead(Component component, IHeaderResponse response) { 16 | ChosenReference.render(response); 17 | String script = String.format("$('#%s').chosen()", component.getMarkupId()); 18 | response.render(OnDomReadyHeaderItem.forScript(script)); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/components/references/ChosenReference.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.components.references; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.apache.wicket.bootstrap.Bootstrap; 6 | import org.apache.wicket.markup.head.CssHeaderItem; 7 | import org.apache.wicket.markup.head.HeaderItem; 8 | import org.apache.wicket.markup.head.IHeaderResponse; 9 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 10 | import org.apache.wicket.request.resource.CssResourceReference; 11 | import org.apache.wicket.request.resource.JavaScriptResourceReference; 12 | 13 | public class ChosenReference extends JavaScriptResourceReference { 14 | 15 | private static final long serialVersionUID = 1L; 16 | 17 | private static ChosenReference instance = new ChosenReference(); 18 | 19 | public ChosenReference() { 20 | super(ChosenReference.class, "chosen.jquery.min.js"); 21 | } 22 | 23 | @Override 24 | public Iterable getDependencies() { 25 | return Arrays. asList(JavaScriptHeaderItem.forReference(Bootstrap.responsive()), 26 | CssHeaderItem.forReference(new CssResourceReference(ChosenReference.class, "chosen.css"))); 27 | } 28 | 29 | public static void render(IHeaderResponse response) { 30 | response.render(JavaScriptHeaderItem.forReference(instance)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/components/references/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Athou/GeekBot/0c9f05db02537cdc2ea0ddbdc5bd2d34b7b71c9d/src/main/java/be/hehehe/geekbot/web/components/references/chosen-sprite.png -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/components/references/chosen.css: -------------------------------------------------------------------------------- 1 | /* @group Base */ 2 | .chzn-container { 3 | font-size: 13px; 4 | position: relative; 5 | display: inline-block; 6 | zoom: 1; 7 | *display: inline; 8 | } 9 | .chzn-container .chzn-drop { 10 | background: #fff; 11 | border: 1px solid #aaa; 12 | border-top: 0; 13 | position: absolute; 14 | top: 29px; 15 | left: 0; 16 | -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15); 17 | -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15); 18 | box-shadow : 0 4px 5px rgba(0,0,0,.15); 19 | z-index: 1010; 20 | } 21 | /* @end */ 22 | 23 | /* @group Single Chosen */ 24 | .chzn-container-single .chzn-single { 25 | background-color: #ffffff; 26 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 ); 27 | background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4)); 28 | background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); 29 | background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); 30 | background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); 31 | background-image: linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); 32 | -webkit-border-radius: 5px; 33 | -moz-border-radius : 5px; 34 | border-radius : 5px; 35 | -moz-background-clip : padding; 36 | -webkit-background-clip: padding-box; 37 | background-clip : padding-box; 38 | border: 1px solid #aaaaaa; 39 | -webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); 40 | -moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); 41 | box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); 42 | display: block; 43 | overflow: hidden; 44 | white-space: nowrap; 45 | position: relative; 46 | height: 23px; 47 | line-height: 24px; 48 | padding: 0 0 0 8px; 49 | color: #444444; 50 | text-decoration: none; 51 | } 52 | .chzn-container-single .chzn-default { 53 | color: #999; 54 | } 55 | .chzn-container-single .chzn-single span { 56 | margin-right: 26px; 57 | display: block; 58 | overflow: hidden; 59 | white-space: nowrap; 60 | -o-text-overflow: ellipsis; 61 | -ms-text-overflow: ellipsis; 62 | text-overflow: ellipsis; 63 | } 64 | .chzn-container-single .chzn-single abbr { 65 | display: block; 66 | position: absolute; 67 | right: 26px; 68 | top: 6px; 69 | width: 12px; 70 | height: 13px; 71 | font-size: 1px; 72 | background: url('chosen-sprite.png') right top no-repeat; 73 | } 74 | .chzn-container-single .chzn-single abbr:hover { 75 | background-position: right -11px; 76 | } 77 | .chzn-container-single.chzn-disabled .chzn-single abbr:hover { 78 | background-position: right top; 79 | } 80 | .chzn-container-single .chzn-single div { 81 | position: absolute; 82 | right: 0; 83 | top: 0; 84 | display: block; 85 | height: 100%; 86 | width: 18px; 87 | } 88 | .chzn-container-single .chzn-single div b { 89 | background: url('chosen-sprite.png') no-repeat 0 0; 90 | display: block; 91 | width: 100%; 92 | height: 100%; 93 | } 94 | .chzn-container-single .chzn-search { 95 | padding: 3px 4px; 96 | position: relative; 97 | margin: 0; 98 | white-space: nowrap; 99 | z-index: 1010; 100 | } 101 | .chzn-container-single .chzn-search input { 102 | background: #fff url('chosen-sprite.png') no-repeat 100% -22px; 103 | background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); 104 | background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 105 | background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 106 | background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 107 | background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(#eeeeee 1%, #ffffff 15%); 108 | margin: 1px 0; 109 | padding: 4px 20px 4px 5px; 110 | outline: 0; 111 | border: 1px solid #aaa; 112 | font-family: sans-serif; 113 | font-size: 1em; 114 | } 115 | .chzn-container-single .chzn-drop { 116 | -webkit-border-radius: 0 0 4px 4px; 117 | -moz-border-radius : 0 0 4px 4px; 118 | border-radius : 0 0 4px 4px; 119 | -moz-background-clip : padding; 120 | -webkit-background-clip: padding-box; 121 | background-clip : padding-box; 122 | } 123 | /* @end */ 124 | 125 | .chzn-container-single-nosearch .chzn-search input { 126 | position: absolute; 127 | left: -9000px; 128 | } 129 | 130 | /* @group Multi Chosen */ 131 | .chzn-container-multi .chzn-choices { 132 | background-color: #fff; 133 | background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); 134 | background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 135 | background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 136 | background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 137 | background-image: linear-gradient(#eeeeee 1%, #ffffff 15%); 138 | border: 1px solid #aaa; 139 | margin: 0; 140 | padding: 0; 141 | cursor: text; 142 | overflow: hidden; 143 | height: auto !important; 144 | height: 1%; 145 | position: relative; 146 | } 147 | .chzn-container-multi .chzn-choices li { 148 | float: left; 149 | list-style: none; 150 | } 151 | .chzn-container-multi .chzn-choices .search-field { 152 | white-space: nowrap; 153 | margin: 0; 154 | padding: 0; 155 | } 156 | .chzn-container-multi .chzn-choices .search-field input { 157 | color: #666; 158 | background: transparent !important; 159 | border: 0 !important; 160 | font-family: sans-serif; 161 | font-size: 100%; 162 | height: 15px; 163 | padding: 5px; 164 | margin: 1px 0; 165 | outline: 0; 166 | -webkit-box-shadow: none; 167 | -moz-box-shadow : none; 168 | box-shadow : none; 169 | } 170 | .chzn-container-multi .chzn-choices .search-field .default { 171 | color: #999; 172 | } 173 | .chzn-container-multi .chzn-choices .search-choice { 174 | -webkit-border-radius: 3px; 175 | -moz-border-radius : 3px; 176 | border-radius : 3px; 177 | -moz-background-clip : padding; 178 | -webkit-background-clip: padding-box; 179 | background-clip : padding-box; 180 | background-color: #e4e4e4; 181 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); 182 | background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); 183 | background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 184 | background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 185 | background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 186 | background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 187 | -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); 188 | -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); 189 | box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); 190 | color: #333; 191 | border: 1px solid #aaaaaa; 192 | line-height: 13px; 193 | padding: 3px 20px 3px 5px; 194 | margin: 3px 0 3px 5px; 195 | position: relative; 196 | cursor: default; 197 | } 198 | .chzn-container-multi .chzn-choices .search-choice-focus { 199 | background: #d4d4d4; 200 | } 201 | .chzn-container-multi .chzn-choices .search-choice .search-choice-close { 202 | display: block; 203 | position: absolute; 204 | right: 3px; 205 | top: 4px; 206 | width: 12px; 207 | height: 13px; 208 | font-size: 1px; 209 | background: url('chosen-sprite.png') right top no-repeat; 210 | } 211 | .chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover { 212 | background-position: right -11px; 213 | } 214 | .chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close { 215 | background-position: right -11px; 216 | } 217 | /* @end */ 218 | 219 | /* @group Results */ 220 | .chzn-container .chzn-results { 221 | margin: 0 4px 4px 0; 222 | max-height: 240px; 223 | padding: 0 0 0 4px; 224 | position: relative; 225 | overflow-x: hidden; 226 | overflow-y: auto; 227 | -webkit-overflow-scrolling: touch; 228 | } 229 | .chzn-container-multi .chzn-results { 230 | margin: -1px 0 0; 231 | padding: 0; 232 | } 233 | .chzn-container .chzn-results li { 234 | display: none; 235 | line-height: 15px; 236 | padding: 5px 6px; 237 | margin: 0; 238 | list-style: none; 239 | } 240 | .chzn-container .chzn-results .active-result { 241 | cursor: pointer; 242 | display: list-item; 243 | } 244 | .chzn-container .chzn-results .highlighted { 245 | background-color: #3875d7; 246 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 ); 247 | background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); 248 | background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%); 249 | background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%); 250 | background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%); 251 | background-image: linear-gradient(#3875d7 20%, #2a62bc 90%); 252 | color: #fff; 253 | } 254 | .chzn-container .chzn-results li em { 255 | background: #feffde; 256 | font-style: normal; 257 | } 258 | .chzn-container .chzn-results .highlighted em { 259 | background: transparent; 260 | } 261 | .chzn-container .chzn-results .no-results { 262 | background: #f4f4f4; 263 | display: list-item; 264 | } 265 | .chzn-container .chzn-results .group-result { 266 | cursor: default; 267 | color: #999; 268 | font-weight: bold; 269 | } 270 | .chzn-container .chzn-results .group-option { 271 | padding-left: 15px; 272 | } 273 | .chzn-container-multi .chzn-drop .result-selected { 274 | display: none; 275 | } 276 | .chzn-container .chzn-results-scroll { 277 | background: white; 278 | margin: 0 4px; 279 | position: absolute; 280 | text-align: center; 281 | width: 321px; /* This should by dynamic with js */ 282 | z-index: 1; 283 | } 284 | .chzn-container .chzn-results-scroll span { 285 | display: inline-block; 286 | height: 17px; 287 | text-indent: -5000px; 288 | width: 9px; 289 | } 290 | .chzn-container .chzn-results-scroll-down { 291 | bottom: 0; 292 | } 293 | .chzn-container .chzn-results-scroll-down span { 294 | background: url('chosen-sprite.png') no-repeat -4px -3px; 295 | } 296 | .chzn-container .chzn-results-scroll-up span { 297 | background: url('chosen-sprite.png') no-repeat -22px -3px; 298 | } 299 | /* @end */ 300 | 301 | /* @group Active */ 302 | .chzn-container-active .chzn-single { 303 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); 304 | -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); 305 | box-shadow : 0 0 5px rgba(0,0,0,.3); 306 | border: 1px solid #5897fb; 307 | } 308 | .chzn-container-active .chzn-single-with-drop { 309 | border: 1px solid #aaa; 310 | -webkit-box-shadow: 0 1px 0 #fff inset; 311 | -moz-box-shadow : 0 1px 0 #fff inset; 312 | box-shadow : 0 1px 0 #fff inset; 313 | background-color: #eee; 314 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 ); 315 | background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff)); 316 | background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%); 317 | background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%); 318 | background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%); 319 | background-image: linear-gradient(#eeeeee 20%, #ffffff 80%); 320 | -webkit-border-bottom-left-radius : 0; 321 | -webkit-border-bottom-right-radius: 0; 322 | -moz-border-radius-bottomleft : 0; 323 | -moz-border-radius-bottomright: 0; 324 | border-bottom-left-radius : 0; 325 | border-bottom-right-radius: 0; 326 | } 327 | .chzn-container-active .chzn-single-with-drop div { 328 | background: transparent; 329 | border-left: none; 330 | } 331 | .chzn-container-active .chzn-single-with-drop div b { 332 | background-position: -18px 1px; 333 | } 334 | .chzn-container-active .chzn-choices { 335 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); 336 | -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); 337 | box-shadow : 0 0 5px rgba(0,0,0,.3); 338 | border: 1px solid #5897fb; 339 | } 340 | .chzn-container-active .chzn-choices .search-field input { 341 | color: #111 !important; 342 | } 343 | /* @end */ 344 | 345 | /* @group Disabled Support */ 346 | .chzn-disabled { 347 | cursor: default; 348 | opacity:0.5 !important; 349 | } 350 | .chzn-disabled .chzn-single { 351 | cursor: default; 352 | } 353 | .chzn-disabled .chzn-choices .search-choice .search-choice-close { 354 | cursor: default; 355 | } 356 | 357 | /* @group Right to Left */ 358 | .chzn-rtl { text-align: right; } 359 | .chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; } 360 | .chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; } 361 | 362 | .chzn-rtl .chzn-single div { left: 3px; right: auto; } 363 | .chzn-rtl .chzn-single abbr { 364 | left: 26px; 365 | right: auto; 366 | } 367 | .chzn-rtl .chzn-choices .search-field input { direction: rtl; } 368 | .chzn-rtl .chzn-choices li { float: right; } 369 | .chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; } 370 | .chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; background-position: right top;} 371 | .chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; } 372 | .chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; } 373 | .chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; } 374 | .chzn-rtl .chzn-search input { 375 | background: #fff url('chosen-sprite.png') no-repeat -38px -22px; 376 | background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); 377 | background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 378 | background: url('chosen-sprite.png') no-repeat -38px -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 379 | background: url('chosen-sprite.png') no-repeat -38px -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 380 | background: url('chosen-sprite.png') no-repeat -38px -22px, linear-gradient(#eeeeee 1%, #ffffff 15%); 381 | padding: 4px 5px 4px 20px; 382 | direction: rtl; 383 | } 384 | /* @end */ 385 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/nav/NavigationHeader.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 |
  • 8 |
    9 | 10 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/nav/NavigationHeader.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.nav; 2 | 3 | import java.util.Collection; 4 | 5 | import org.apache.wicket.markup.html.basic.Label; 6 | import org.apache.wicket.markup.html.panel.Panel; 7 | import org.apache.wicket.markup.repeater.RepeatingView; 8 | 9 | import be.hehehe.geekbot.web.TemplatePage.PageModel; 10 | 11 | @SuppressWarnings("serial") 12 | public class NavigationHeader extends Panel { 13 | 14 | public NavigationHeader(String id, String headerName, Collection pages) { 15 | super(id); 16 | add(new Label("header", headerName)); 17 | 18 | RepeatingView repeatingView = new RepeatingView("li"); 19 | for (PageModel page : pages) { 20 | repeatingView.add(new NavigationItem(repeatingView.newChildId(), page)); 21 | } 22 | add(repeatingView); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/nav/NavigationItem.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/nav/NavigationItem.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.nav; 2 | 3 | import org.apache.wicket.AttributeModifier; 4 | import org.apache.wicket.markup.html.basic.Label; 5 | import org.apache.wicket.markup.html.link.BookmarkablePageLink; 6 | import org.apache.wicket.markup.html.panel.Panel; 7 | import org.apache.wicket.model.AbstractReadOnlyModel; 8 | 9 | import be.hehehe.geekbot.web.TemplatePage; 10 | import be.hehehe.geekbot.web.TemplatePage.PageModel; 11 | 12 | @SuppressWarnings("serial") 13 | public class NavigationItem extends Panel { 14 | 15 | public NavigationItem(String id, PageModel page) { 16 | super(id); 17 | final Class pageClass = page.getPageClass(); 18 | 19 | add(new AttributeModifier("class", new AbstractReadOnlyModel() { 20 | public String getObject() { 21 | return getPage().getClass().equals(pageClass) ? "active" : AttributeModifier.VALUELESS_ATTRIBUTE_REMOVE; 22 | } 23 | })); 24 | 25 | BookmarkablePageLink pageLink = new BookmarkablePageLink("a", page.getPageClass()); 26 | add(pageLink); 27 | 28 | pageLink.add(new Label("link-name", page.getName())); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/utils/ModelFactory.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.utils; 2 | 3 | import org.apache.wicket.model.PropertyModel; 4 | 5 | import ch.lambdaj.Lambda; 6 | import ch.lambdaj.function.argument.Argument; 7 | import ch.lambdaj.function.argument.ArgumentsFactory; 8 | 9 | public class ModelFactory { 10 | 11 | public static PropertyModel model(Object value, T proxiedValue) { 12 | Argument a = ArgumentsFactory.actualArgument(proxiedValue); 13 | String invokedPN = a.getInkvokedPropertyName(); 14 | PropertyModel m = new PropertyModel(value, invokedPN); 15 | return m; 16 | } 17 | 18 | @SuppressWarnings("unchecked") 19 | public static T proxy(T t) { 20 | Object object = Lambda.on(t.getClass()); 21 | return (T) object; 22 | } 23 | 24 | public static T proxy(Class clazz) { 25 | return Lambda.on(clazz); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/be/hehehe/geekbot/web/utils/WicketUtils.java: -------------------------------------------------------------------------------- 1 | package be.hehehe.geekbot.web.utils; 2 | 3 | import java.util.Map; 4 | 5 | import org.apache.wicket.markup.head.CssHeaderItem; 6 | import org.apache.wicket.markup.head.IHeaderResponse; 7 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 8 | import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 9 | import org.apache.wicket.request.resource.CssResourceReference; 10 | import org.apache.wicket.request.resource.JavaScriptResourceReference; 11 | import org.apache.wicket.util.io.IOUtils; 12 | import org.apache.wicket.util.template.PackageTextTemplate; 13 | 14 | public class WicketUtils { 15 | 16 | public static JavaScriptHeaderItem buildJavaScriptHeaderItem(Class klass) { 17 | return JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(klass, klass.getSimpleName() + ".js")); 18 | } 19 | 20 | public static void loadJS(IHeaderResponse response, Class klass) { 21 | response.render(buildJavaScriptHeaderItem(klass)); 22 | } 23 | 24 | public static void loadJS(IHeaderResponse response, Class klass, Map variables) { 25 | OnDomReadyHeaderItem result = null; 26 | PackageTextTemplate template = null; 27 | try { 28 | template = new PackageTextTemplate(klass, klass.getSimpleName() + ".js"); 29 | String script = template.asString(variables); 30 | result = OnDomReadyHeaderItem.forScript(script); 31 | } finally { 32 | IOUtils.closeQuietly(template); 33 | } 34 | response.render(result); 35 | } 36 | 37 | public static CssHeaderItem buildCssHeaderItem(Class klass) { 38 | return CssHeaderItem.forReference(new CssResourceReference(klass, klass.getSimpleName() + ".css")); 39 | } 40 | 41 | public static void loadCSS(IHeaderResponse response, Class klass) { 42 | response.render(buildCssHeaderItem(klass)); 43 | } 44 | 45 | public static void loadCSS(IHeaderResponse response, Class klass, Map variables) { 46 | CssHeaderItem result = null; 47 | PackageTextTemplate template = null; 48 | try { 49 | template = new PackageTextTemplate(klass, klass.getSimpleName() + ".js"); 50 | String css = template.asString(variables); 51 | result = CssHeaderItem.forCSS(css, null); 52 | } finally { 53 | IOUtils.closeQuietly(template); 54 | } 55 | response.render(result); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | jboss/datasources/geekbot 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension: -------------------------------------------------------------------------------- 1 | be.hehehe.geekbot.bot.GeekBotCDIExtension -------------------------------------------------------------------------------- /src/main/resources/config.properties.example: -------------------------------------------------------------------------------- 1 | # Bot nickname 2 | botname=GeekBot 3 | 4 | # administrator password for various operations 5 | admin.password=hello 6 | 7 | # IRC Server to use 8 | server=irc.quakenet.org 9 | port=8080 10 | 11 | # Channel to join 12 | channels=\#botchannel,\#botchannel2 13 | channel.command.botchannel=ALL 14 | channel.command.botchannel2=be.hehehe.geekbot.commands.GoogleCommand,be.hehehe.geekbot.commands.BlagueCommand 15 | 16 | # Web settings 17 | webserver.hostname = http://hostname.com/path/ 18 | webserver.port = 11223 19 | 20 | # Bitly credentials 21 | bitly.login= 22 | bitly.apikey= 23 | 24 | # Imgur credentials 25 | imgur.clientId= 26 | 27 | # VDM api key 28 | vdm.apikey= 29 | 30 | # MemeGenerator config 31 | meme.login= 32 | meme.password= 33 | 34 | # Twitter auth 35 | twitter.consumerKey= 36 | twitter.consumerSecret= 37 | twitter.token= 38 | twitter.tokenSecret= 39 | 40 | # Google auth 41 | google.key = 42 | google.cseId = 43 | 44 | # Discord bot name 45 | discord.botname= 46 | discord.token= -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.logger.be.hehehe.geekbot=DEBUG, CONSOLE, FILE 2 | log4j.logger.org.reflections.Reflections=WARN, CONSOLE 3 | log4j.logger.org.hibernate=WARN, CONSOLE 4 | log4j.logger.org.jboss.weld=WARN, CONSOLE 5 | log4j.logger.org.jboss.logging=WARN, CONSOLE 6 | log4j.logger.java.sql.DatabaseMetaData=WARN, CONSOLE 7 | log4j.logger.org.jboss.interceptor=WARN, CONSOLE 8 | log4j.logger.org.apache.commons=WARN, CONSOLE 9 | log4j.logger.org.eclipse.jetty=WARN, CONSOLE 10 | log4j.logger.com.mchange=WARN, CONSOLE 11 | log4j.logger.org.apache.wicket=WARN, CONSOLE 12 | 13 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 14 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 15 | log4j.appender.CONSOLE.layout.ConversionPattern=%-5p %d{ISO8601} [%c{1}\:%L] %m%n 16 | 17 | log4j.appender.FILE=org.apache.log4j.RollingFileAppender 18 | log4j.appender.FILE.File=geekbot.log 19 | log4j.appender.FILE.layout=org.apache.log4j.PatternLayout 20 | log4j.appender.FILE.layout.ConversionPattern=%-5p %d{ISO8601} [%c{1}\:%L/%t] %m%n 21 | log4j.appender.FILE.MaxFileSize=100KB 22 | log4j.appender.FILE.MaxBackupIndex=5 23 | log4j.appender.FILE.Append=false -------------------------------------------------------------------------------- /src/main/resources/twitch.properties.example: -------------------------------------------------------------------------------- 1 | # List of twitch.tv streams 2 | 3 | # StreamName -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Wicket 8 | org.apache.wicket.protocol.http.WicketFilter 9 | 10 | applicationClassName 11 | be.hehehe.geekbot.web.WicketApplication 12 | 13 | 14 | configuration 15 | deployment 16 | 17 | 18 | 19 | Wicket 20 | /* 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Athou/GeekBot/0c9f05db02537cdc2ea0ddbdc5bd2d34b7b71c9d/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /src/main/webapp/images/accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Athou/GeekBot/0c9f05db02537cdc2ea0ddbdc5bd2d34b7b71c9d/src/main/webapp/images/accept.png -------------------------------------------------------------------------------- /src/main/webapp/images/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Athou/GeekBot/0c9f05db02537cdc2ea0ddbdc5bd2d34b7b71c9d/src/main/webapp/images/cross.png --------------------------------------------------------------------------------