-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
zingmars
committed
Oct 6, 2015
1 parent
92a52d2
commit 4d12905
Showing
29 changed files
with
2,560 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#IntelliJ IDEA gitignore list | ||
.idea/* | ||
*.ipr | ||
*.iws | ||
out/* | ||
.idea_modules/* | ||
atlassian-ide-plugin.xml | ||
com_crashlytics_export_strings.xml | ||
crashlytics.properties | ||
crashlytics-build.properties | ||
logs/* | ||
*.cfg | ||
module_otg-bot.xml | ||
otg-bot.properties | ||
otg-bot.xml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
(29.09.2015) v0.0.0 - Initial commit | ||
(06.10.2015) v1.0.0 - Initial commit to github |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<module type="JAVA_MODULE" version="4"> | ||
<component name="NewModuleRootManager" inherit-compiler-output="true"> | ||
<exclude-output /> | ||
<content url="file://$MODULE_DIR$"> | ||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> | ||
</content> | ||
<orderEntry type="jdk" jdkName="1.7" jdkType="JavaSDK" /> | ||
<orderEntry type="sourceFolder" forTests="false" /> | ||
<orderEntry type="library" exported="" name="KotlinJavaRuntime (2)" level="project" /> | ||
</component> | ||
</module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#Cbox Bot settings file. Please don't touch unless you know specifically what to change, else you might cause a crash. | ||
#Tue Oct 06 10:28:36 UTC 2015 | ||
LastSeen= | ||
StreamerList=zingmars\:0\\0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
Cbox.ws Bot in Kotlin | ||
==== | ||
|
||
# This is a very overengineered "simple" bot that... | ||
|
||
1) Connects to a specified cbox.ws chat box and reads/logs messages | ||
|
||
2) Allows moderator-only commands and that kind of stuff | ||
|
||
3) Provides a CLI (trough, say a telnet client) access that allows quick reconfiguring and reloading if needed (admittedly) | ||
|
||
4) Has a plugin API that lets you inject any compiled kotlin class (technically at any time too) and run it, as long as it inherits the base plugin class and functions | ||
|
||
5) Does it in a multi-threaded fashion | ||
|
||
And probably much more... | ||
|
||
----- | ||
# Why? | ||
|
||
When looking around the webs I found this language, Kotlin. Now, I have a project coming up that involves building an Android app, and not being on good terms with Java myself, I decided to build something simple to understand it's basics (this is why you'll find stuff like classloader, socket server etc. in this project). I've also wanted to build something that connects to a chat box that a community I frequent uses; I've tried making a desktop client before before, but I didn't finish it out of laziness. It should however in theory work with any cbox.ws based chatbox, although I haven't tested it, so I can't vouch for it. | ||
|
||
----- | ||
# Setup guide (using IntelliJ IDEA): | ||
|
||
1) Open File->Settings->Plugins, look for Kotlin, download and install it | ||
|
||
2) Open project's folder using the Open function | ||
|
||
3) On the Project view open the src directory and open the app.kt file | ||
|
||
4) There should now be a bar asking your to set up Project SDK. If you haven't already, point it to your jdk's directory. | ||
|
||
5) I have included kotlin's runtimes with the project, so you should now be able to make and run this project. | ||
|
||
------ | ||
# TL;DR - Files | ||
|
||
/src: | ||
|
||
/BotPlugins - contains plugins for the bot | ||
|
||
/Containers - some cross communication model classes | ||
|
||
app.kt - main entry point for this application. Loads and manages every other class | ||
|
||
The rest should be obvious - ZipUtil handles file zipping (for log file archiving), CLI handles command line input, Logger Logs, HTTP handles HTTP connections etc. | ||
|
||
------ | ||
# Plugins | ||
|
||
Just throw them into /src/BotPlugins. All plugins must follow this base example: | ||
|
||
``` | ||
package BotPlugins | ||
import Containers.PluginBufferItem | ||
public class PluginName : BasePlugin() | ||
{ | ||
override fun pubInit() :Boolean | ||
{ | ||
return true | ||
} | ||
override fun connector(buffer : PluginBufferItem) :Boolean | ||
{ | ||
return true | ||
} | ||
override fun stop() | ||
{ | ||
} | ||
} | ||
``` | ||
|
||
There are couple of notes though: | ||
|
||
1. Plugins are using a really weird ClassLoader implementation, so it it's a bit buggy. The reason for this is that I wanted to see if dynamic class loading is possible with Java, and although it is, I don't really think it's worth the hassle I went through. | ||
|
||
2. Plugin name MUST be the same as the filename. If it's not, it won't be loaded. The files in BotPlugins folder can be empty however, but they need to have a compiled version. | ||
|
||
3. Yes, it must be a class. | ||
|
||
4. pubInit() is the class that's executed once it has been given proper context (namely - logger, a settings file, threadmanager etc). More info on that is available as a comment on BasePlugin.kt | ||
|
||
5. connector() is called for every message | ||
|
||
6. stop() is called when the plugin is turned off. This is for when you have threads or something like that running. | ||
|
||
7. Even though you can use pretty much the whole logger class, it would be saner to use plugin related log commands. The boring one's for when the loglevel is over 2 and it's there to avoid spamming the log with pointless stuff. | ||
|
||
8. Yes, you can reload your class while it's running (using CLI, or if you make it to - through an user command), but you'll need to replace the compiled version in the jar file. It works when run from an IDE, not so much when you have a portable jar file. | ||
|
||
------ | ||
# TODO? | ||
|
||
A full refactor to make the code consistent would be nice. Plenty of TODO's scattered through the code as well. Even so, this is project is, for all intents and purposes, finished. Feel free to fork and do whatever. | ||
|
||
------ | ||
# License | ||
|
||
Please view LICENSE (BSD 2-clause). TL;DR - Do what you want, just include the original license and don't blame me when something breaks |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#OTG Bot settings file. Please don't touch unless you know specifically what to change, else you might cause a crash. | ||
#Sat Oct 03 18:15:13 MSK 2015 | ||
daemonPort=9970 | ||
boxtag=0 | ||
fileLog=true | ||
server=0 | ||
chatLogFile=chat.txt | ||
logChat=true | ||
logFile=log.txt | ||
username=zingmars | ||
boxid=0 | ||
logFolder=logs | ||
password=0 | ||
daemonEnabled=true | ||
isOriginal=False | ||
maxLogs=100 | ||
consoleLog=true | ||
avatar= | ||
refreshRate=4000 | ||
logLevel=1 | ||
enablePlugins=true | ||
pluginLogFile=plugins.txt | ||
pluginDirectory=src/BotPlugins | ||
archiveLogs=true | ||
logRotate=true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* Base plugin class to be inherited from | ||
* Created by zingmars on 04.10.2015. | ||
*/ | ||
package BotPlugins | ||
import Containers.PluginBufferItem | ||
import Settings | ||
import Logger | ||
import Plugins | ||
import ThreadController | ||
|
||
open public class BasePlugin() { | ||
public var settings :Settings? = null | ||
public var logger :Logger? = null | ||
public var handher :Plugins? = null | ||
public var pluginName :String? = null | ||
public var controller :ThreadController? = null | ||
|
||
init { | ||
// This is the base class for all CBot plugins | ||
// To make a plugin just extend this class and put it in src/BotPlugins/ (or whatever is defined in your settings file) directory | ||
// Note - your filename must match your class name and it must be inside the BotPlugins package | ||
// To initiate just override pubInit() (you can override this initializer too, but you won't have access to any variables at that point), and to do your logic just override connector. | ||
// Note that your connector override will need to have the PluginBufferItem input for it to receive any messages. | ||
// To send data back just add an element to any of the buffers available in ThreadController, (i.e. BoxBuffer will output anything send to it) | ||
// Note that pubInit and connector both return a boolean value that indicates whether or not the plugin was successful | ||
} | ||
//non-overridable classes | ||
final public fun initiate(settings :Settings, logger: Logger, pluginsHandler :Plugins, controller :ThreadController,pluginName :String) :Boolean | ||
{ | ||
this.settings = settings | ||
this.logger = logger | ||
this.handher = pluginsHandler | ||
this.pluginName = pluginName | ||
this.controller = controller | ||
if(this.pubInit()) this.logger?.LogPlugin(pluginName, "Started!") | ||
else { | ||
this.stop() | ||
this.logger?.LogPlugin(pluginName, "failed to load!") | ||
return false | ||
} | ||
return true | ||
} | ||
//overridable classes | ||
open public fun pubInit() :Boolean { return true } //Initializer | ||
open public fun connector(buffer :PluginBufferItem) :Boolean { return true } //Receives data from Plugin controller | ||
open public fun stop() {} //This is run when the plugin is unloaded | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/** | ||
* Checks chat for user written messages and responds to specific queries | ||
* Created by zingmars on 04.10.2015. | ||
*/ | ||
package BotPlugins | ||
import Containers.PluginBufferItem | ||
import java.util.* | ||
|
||
public class AdminCommands : BasePlugin() | ||
{ | ||
private var DB : HashMap<String, String> = HashMap() | ||
override fun pubInit() :Boolean | ||
{ | ||
try { | ||
if(handher?.isAdmin() == false) { | ||
throw Exception("User does not have mod rights to the given box. Exiting.") | ||
} | ||
|
||
settings?.checkSetting("IPDB", true) | ||
var savedDB = settings?.GetSetting("LastSeen").toString() | ||
|
||
if(savedDB != "") { | ||
var data = savedDB.split(",") | ||
for(user in data) { | ||
var IP = savedDB.split(":")[0] | ||
var Usernames = savedDB.split(":")[1] | ||
DB.put(IP, Usernames) | ||
} | ||
} | ||
return true | ||
} catch (e: Exception) { | ||
logger?.LogPlugin(pluginName.toString(), "Error: " + e.toString()) | ||
return false | ||
} | ||
} | ||
override fun connector(buffer : PluginBufferItem) :Boolean | ||
{ | ||
var message = buffer.message.split(" ") | ||
var changed :Boolean | ||
//TODO: Rewrite, this is a horrible 2AM energy drink powered way to do this. | ||
if(DB.containsKey(buffer.extradata)) { | ||
//Remove from the old entry | ||
var keys = DB.keySet().iterator() | ||
while(keys.hasNext()) | ||
{ | ||
var key = keys.next() | ||
var userlist = DB.get(key) | ||
if(userlist != null) { | ||
var replaceableString = buffer.userName | ||
if(userlist.indexOf(";"+buffer.userName) > 0) replaceableString = ";" + replaceableString | ||
if (userlist.contains(buffer.userName)) { | ||
userlist.replace(replaceableString, "") | ||
} | ||
} | ||
} | ||
//Add to DB | ||
var entry = DB.get(buffer.extradata) | ||
if (entry != null && !entry.contains(buffer.userName)) DB.set(buffer.userName, ";"+buffer.extradata) | ||
changed = true | ||
} else { | ||
logger?.LogPlugin(this.pluginName.toString(), "New user encountered: " + buffer.userName) | ||
DB.put(buffer.userName, buffer.extradata) | ||
changed = true | ||
} | ||
|
||
if(changed) { | ||
var data = "" | ||
var keys = DB.keySet().iterator() | ||
while(keys.hasNext()) { | ||
var key = keys.next() | ||
var user = DB.get(key) | ||
data += key + ":" + user | ||
} | ||
settings?.SetSetting("IPDB", data) | ||
} | ||
|
||
if(message[0].toLowerCase() == "@alias") { | ||
var keys = DB.keySet().iterator() | ||
while(keys.hasNext()) | ||
{ | ||
var key = keys.next() | ||
var userlist = DB.get(key) | ||
if(userlist != null) { | ||
if(userlist.contains(message[1])) { | ||
controller?.AddToBoxBuffer("User aliases: " + userlist) | ||
break | ||
} else { | ||
controller?.AddToBoxBuffer("No data") | ||
logger?.LogPlugin(this.pluginName.toString(), "Error: Could not find data for " + message[1]) | ||
} | ||
} | ||
} | ||
} | ||
return true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/** | ||
* Stores last cbox activity and posts about it if records are broken | ||
* Created by zingmars on 04.10.2015. | ||
*/ | ||
package BotPlugins | ||
import Containers.PluginBufferItem | ||
|
||
public class DeadboxCheck : BasePlugin() | ||
{ | ||
private var RecordUsername = "" | ||
private var RecordTime = 0L | ||
private var lastMessage = 0L | ||
override fun pubInit() :Boolean | ||
{ | ||
try { | ||
settings?.checkSetting("DeadBox", true) | ||
var savedData = settings?.GetSetting("DeadBox").toString() | ||
|
||
if(savedData != "") { | ||
var data = savedData.split(",") | ||
RecordUsername = data[0] | ||
RecordTime = data[1].toLong() | ||
} | ||
return true | ||
} catch (e: Exception) { | ||
logger?.LogPlugin(this.pluginName.toString(), "Error: " + e.toString()) | ||
return false | ||
} | ||
} | ||
override fun connector(buffer : PluginBufferItem) :Boolean | ||
{ | ||
var timeSinceLast = buffer.time.toLong() - lastMessage | ||
if(lastMessage != 0L && timeSinceLast >= 3600L) { | ||
if(timeSinceLast > RecordTime) { | ||
RecordTime = timeSinceLast | ||
RecordUsername = buffer.userName | ||
controller?.AddToBoxBuffer("Congratz " + buffer.userName + "! You just revived the box and set a new record doing so! This deadbox lasted " + (timeSinceLast.toDouble()/60).toString() + " minutes.") | ||
saveData() | ||
} else { | ||
controller?.AddToBoxBuffer("Congratz " + buffer.userName + "! You just revived the box. This deadbox lasted " + (timeSinceLast.toDouble()/60).toString() + " minutes. The longest recorded deadbox was " + (RecordTime.toDouble()/60).toString() + " minutes long and it was broken by " + RecordUsername) | ||
} | ||
} | ||
return true | ||
} | ||
private fun saveData() | ||
{ | ||
settings?.SetSetting("DeadBox", RecordUsername+","+RecordTime.toString()) | ||
} | ||
} |
Oops, something went wrong.