Rubot/Documentation

From Botdom Wiki

Jump to: navigation, search

It is suggested that this page should be moved to Botdom Documentation.

Please do not move the page by hand. It will be moved by an administrator with the full edit history. In the meantime, you may continue to edit the page as normal. You can discuss this decision on the talk page.
Rubot has a lot of documentation included with it, spread over several files.

Contents

General documentation

README.txt

====== ===== ===== ===== About Rubot ===== ===== ===== ====== 

I hope you enjoy rubot, its taken near a year, countless late nights, lots of 
caffeine and tons of supporting friends to pull this off.

Starting with a php pseudo-threaded design, which I named zBot. Soon after
realizing that I couldn't overcome certain IPC issues without lots of
overhead I moved it to a modular, object oriented design. Finishing zBot
for release with OO interface, dAmnBot compatability API and a very
efficent modular plugin and extension system I was happy, though zBot's
permissions model was never completed.

Shortly there after I became interested in ruby, and decided to learn it.
I ported the entire zBot code base to ruby. After massive refactoring,
a more modular architecture and a formal standard for the dAmnSock
interface I ended up with rubot. So here it is, enjoy. :)

If you find a bug please note Zeros-Elipticus, this IS a beta-release 
version though so don't expect it to be perfect.

If there is a feature that you'd like to see added send me a note too, 
Rubot is a constant work in progress.

Note: Some features may require a *nix system

 - Elliott

====== ===== ===== ====== Warranty ====== ===== ===== ======

This software comes with ABSOLUTELY NO WARRANTY. It is distributed as is. The 
license this software is licensed under can be found in LICENSE.txt.

===== ===== ===== ======  Thank Yous ====== ===== ===== ====

Some components of this software are based on dAmnlib by Kevin Wallace
(doofsmack). The original interface was written in php, and then ported
to ruby. Major thanks go to him for the excellent design of dAmnLib
and for support.

Components:

	dAmnClient class
	dAmnChannel class
	dAmnUser class
	dAmnPacket classes

===== ===== ===== ===== ===== ===== ===== ===== ===== ======

Special thanks to:
   Kevin Wallace [`doofsmack]
	- dAmnlib, dAmn packet reference, and lots of other help
   Stanford Chau [=stfc]
	- inspiration, regular expression help, and lot of brainstorming
   Teddy Wexler  [=twexler]
	- general help with dAmn protocol, testing and Linux support
   Kroc Camen [=Kroc]
	- general inspiration and motivation to write well documented code
   Natalie Julke [=thegnat]
	- For putting up with me always coding, long into the night like
	  a mad man <3


===== ===== ===== ===== ===== ===== ===== ===== ===== ======

license.txt

/* ##################################################

	< License >
		
	This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License
	http://creativecommons.org/licenses/by-nc-sa/2.5/
	
	You CAN:
	    * Create derivitive works, provided you:
	        * attribute the original author with a link to their website
	        * the derivitive is not used for commerical purposes
	        * distribute the derivitive works under the same license
	    * Distribute the code, provided you:
	        * attribute the original author with a link to their website
	        * the code is not used for commerical purposes
	        * distribute the code under the same license
	
	You CAN NOT:
	    * Use this code, or derivitives for commerical purposes.
	    * Remove this notice
	
	Title: Rubot (OO bot for deviantART Message Network)
	Date: 2006
	Creator: Elliott Sprehn (http://enfinitystudios.thaposse.net elliott@thaposse.net)
	Original Source: Send an email.

	< Information >

	This set of classes, extensions and plugins provides an easily to maintained and extended
	bot for the deviantART Message Network also known as dAmn.
	
	Author:  	Elliott Sprehn
	Version: 	1.0 Beta-0198 Beta Tester Release
	Date:    	June 28 2006
	
	< Requirements >
	
	Ruby 1.8.2+
	
	< Changelog >
	
	[28-06-2006]
		Beta Tester Release version.
	
	< Quote >
	"Code well, code to standards, and above all.. code for fun." 
	
################################################## */

Guide.txt

###########
#
# Table Of Contents
#
##

1. Install Ruby
2. Install Rubot
3. Setup Rubot
4. Starting Rubot
5. Changing Permissions 

####
# 1. Install Ruby
##

1a. OS X
	If you're on OS X you already have ruby! Doesn't hurt to install Ruby 1.8.4
	though, so check out the below link and follow the secition on building ruby:
	http://hivelogic.com/articles/2005/12/01/ruby_rails_lighttpd_mysql_tiger
	
1b. Windows
	If you're on Windows go have a look at the following links:
	
	http://rubyinstaller.rubyforge.org/wiki/wiki.pl
	http://rubyforge.org/projects/rubyinstaller/
	
	Download the latest installer to your desktop and install ruby. It should
	associate .rb files with ruby so launching the bot should be easy.

1c. *nix
	If you're on a *nix platform the installation steps may vary, its best to check
	if there is a ruby package for your platform first before you try and compile it.
	Rubot has been tested to work well with Ruby 1.8.1 on OpenBSD (in fact thats what
	~rubot is running on).
	
####
# 2. Install Rubot
##

Unpack the rubot.zip to a directory. It doesn't matter where really. You'll probably have a 
rubot folder and a __MACOSX folder. The __MACOSX folder is resource forks from my mac, if 
you're on Windows or *nix go ahead and delete it. 

Now you'll have a directory structure similar to:

config/
core/
docs/
modules/
plugins/
startup.rb

I'll talk about what each folder is for in a later section.

startup.rb is the script that launches the bot, you'll use this later. For now lets get on
to the configuration.

####
# 3. Setup Rubot
##

Setting up rubot is extremely simple. In the root of the rubot application directory there 
is a folder called config. In that there is a file called main.conf.rb; This file holds the
configuration options.

It explains what each of them do in the file, but I'll go over it here too:

RUBOT_USERNAME:
	This is the username that will be used to connect to dAmn. It must be a valid
	deviantART user account. It should be a string, so put single or double quotes around
	it. For example:

	
	RUBOT_USERNAME = 'Zeros-Elipticus'
	
RUBOT_AUTHTOKEN:
	This is the authtoken used to login to dAmn. If you want to get your authtoken login
	to deviantART with the account specified in RUBOT_USERNAME. Then open the page
	http://chat.deviantart.com/ and join a channel. Now look at the source, you'll see a
	line like (all on one line):

	initdAmnChat("chat.deviantart.com", 
	             "chat:damnishies", 
	             "Zeros-Elipticus", 
	             "3717c78b9d9e6a47278a8e69edb636e9"); 

	The last portion is your authtoken. Its a 32bit hash that deviantART generates when
	you login and its a great deal more secure to use that to have your bot connect.
	In the event the bot's source is stolen and someone gets the authtoken you can
	login as the bot in a browser and force the dA to generate a new authtoken making
	the stolen one useless.

	If you absolutely must you can supply a password here instead of an authtoken and
	rubot will get the authtoken for you when it attempts to login to dAmn. This is not
	recommended though.
	
	Example:

		RUBOT_AUTHTOKEN = 'd88bcd894fea88263daab2df523e415e'
		RUBOT_AUTHTOKEN = 'my$7r0ngPa$$0w0rd'


RUBOT_DEFAULT_CHANNELS:
	This is an array of default channels to join when the bot connects to dAmn, they should
	be specified in an array format. The format of the channel names doesn't matter, 
	'chat:damnishies', '#damnishies' and 'damnishies' are all valid.
	
	Example:
		
		RUBOT_DEFAULT_CHANNELS = ['#botdom','chat:damnishies','trivia']
		
RUBOT_TRIGGER:
	This is the trigger that the bot will look for when checking for commands. For example
	if the trigger is '!' when someone types 'about' nothing will happen, but is someone
	types '!about' rubot will look for the about plugin and try and execute it if the
	user has permissions to do so. (more about permissions later)
		
	There are no reserved characers beyond what ruby strings allow, and the trigger can
	be of any length.
		
	Example:
	
		RUBOT_TRIGGER = '--'
		
RUBOT_OWNER:
	The owner is a user with complete access to all plugins. Set this carefully, as it
	gives the user total control.
	
	Example:
		
		RUBOT_OWNER = 'Zeros-Elipticus'		
		
RUBOT_CONSOLE_ANSICOLOR:
	This determines if messages output to the terminal should be in color or if
	only plain text messages should be output. Personally I like color messges as it
	makes it easy to tell what type of message is what. Should be either a boolean
	true or a boolean false.
	
	Example:
		
		RUBOT_CONSOLE_ANSICOLOR = true	
	
RUBOT_REJOIN_ON_KICK:
	This controls if the bot will rejoin when it is kicked from a channel. Set to true
	if you want it to rejoin when its kicked, false if you don't.
	
	Example:
	
		RUBOT_REJOIN_ON_KICK = true

####
# 4. Starting Rubot
##

Starting up the bot is quick and simple!

4a. OS X
	On OS X open /Applications/Utilities/Terminal.app; then using the cd command go the
	directory where you unpacked rubot. Type ./startup.rb and hit enter. The bot should
	start right up.
	
4b. Windows
	Open a cmd.exe window and cd into the directory where you unpacked rubot. Type the path to
	the ruby executable (for example: C:\ruby\ruby.exe) and then a space and then startup.rb; 
	the bot should start right up. Optionally, depending on how you installed ruby you may
	be able to just double click the startup.rb icon to start the bot.

4c. *nix
	Open a terminal and then use the Mac OS X instructions above for what to type.

####
# 5. Changing permissions
##

!privman listusers
!privman listchannels

!privman showuser [username]
!privman adduser [username] [groups...]
!privman remuser [username]

!privman showchannel [channel]
!privman addchannel [channel]
!privman remchannel [channel]

!privman showprivclass [channel] [privclass]
!privman addprivclass [channel] [privclass] [groups...]
!privman remprivclass [channel] [privclass]

When [groups...] are specified it means that a comma seperated list of privelege groups needs
to be provided to the command, there should be no spaced between the comma and the list items.

Example:

add a user with some access groups:
	!privman adduser thegnat factfun,admin,basic
	
show a user:
	!privman showuser thegnat

remove a user:
	!privman remuser thegnat

add a channel, channels must be added before they can have privclasses added to them:
	!privman addchannel botdom
	
add a privclass with some access groups:
	!privman addprivclass botdom the-bot-team admin,basic

show the privclass:
	!privman showprivclass botdom the-bot-team

remove the privclass:
	!privman remprivclass botdom

show the channel:
	!privman showchannel botdom

remove the channel:
	!privman remchannel botdom

Adding privelege groups is a bit more complicated. Right now the only way to do this is to
edit the config/permissions.xml file and add the group manually. I'll be adding an easy
interface for this shortly.

To add a group in the XML you need to follow the following steps:

* Open the permissions.xml
* Locate the <groups> element, you will add a new group inside it.
* Create a new <group> element with an attribute 'id' where the value is the group name.
	ex. <group id="factfun">
* Inside the <group> element create a <plugin> element for each plugin you want that
  group to have access to. The 'name' attribute of the <plugin> element identifies the
  plugin name, it should match the folder the plugin cmd.rb is in.
* Inside the <plugin> element create an <access> element. 

  If you want to give complete access to that plugin use the keyword ALL_ACCESS. Place that
  inside the <access> element. If you only want the person to be able to use the default 
  command for the plugin, which is called when no subcommand is specified, then use 
  DEFAULT_ACCESS in the same way.
  
  If you want more granular control you can add subcommands to the <access> element, each in
  their own <allow> element.
  
* Make sure all elements are closed, this MUST be valid XML.

Example:

<!-- Create the group basic -->
<group id="basic">
	
	<!-- eightball plugin with complete access -->
	<plugin name="eightball">
		<access>ALL_ACCESS</access>
	</plugin>
	
	<!-- randfact plugin with default access, no sub commands allowed -->
	<plugin name="randfact">
		<access>DEFAULT_ACCESS</access>
	</plugin>
	
	<!-- info plugin, only allow the perm and list sub commands -->
	<plugin name="info">
		<access>
			<allow>perm</allow>
			<allow>list</allow>
		</access>
	</plugin>
</group>

--

Really sorry this is so complicated, I'm working a nice interface to simplify this!!!! 
 - Elliott

plugins.txt

about - version information about the bot
eightball - simple port of the noodlebot eightball, example that porting is easy
info - gives info about what channels the bot is in, gets topic and title
join - joins channel
me - does a \me in a channel
part - parts a channel
plugin - manage plugins, list and unload them
privman - manage privelege groups, plugin access et al.
randfact - gets a random fact about a person from the Random Fact Generator
say - Says something in a channel
sys - System utilities, restart, shutdown, process info. Not fully windows compatible, working on improving this.
thread - information about running threads, thread management
validate - validate a website's html and css

Development documentation

API Reference.txt

rubot is based on the Object Oriented programming model.

Below is an API quick reference:

packet.channel.msg("hello!")
packet.channel.npmsg("hello!")					# non parsed message
packet.channel.action("hello!")
packet.channel.users['rubot'].kick("reason")	 		# privclass is optional
packet.channel.users['rubot'].promote("privclass")	 	# privclass is optional
packet.channel.users['rubot'].demote("privclass")		# privclass is optional
packet.channel.part()
packet.channel.setTopic("foo bar")
packet.channel.setTitle("foo bar")

packet.from		# username when its a msg/action packet
packet.ns		# channel namespace ex. "chat:devart"
packet.msg		# the actual message
packet.target		# username when its a join or part packet
packet.args		# array of packet arguments

packet.channel.properties[:title].value
packet.channel.properties[:title].by
packet.channel.properties[:title].ts
packet.channel.properties[:topic].value
packet.channel.properties[:topic].by
packet.channel.properties[:topic].ts

packet.channel.properties[:privclasses][99][:name]			# gets the name of privclass at order 99
packet.channel.properties[:privclasses][99][:members] 			# same as /admin show privclass 99

dAmn.channels['chat:devart'].users['rubot'].promote("privclass") 	#privclass is optional
etc...

dAmn.users['rubot'].channels['chat:devart'].msg("hello") 
etc...

dAmn.join("chat:devart")
dAmn.msg("chat:devart","hello!")
dAmn.npmsg("chat:devart","hello!")			# non parsed message
dAmn.action("chat:devart","hello!")
dAmn.kick("chat:devart","reason")
dAmn.part("chat:devart")
dAmn.promote("chat:devart","rubot","privclass") 	# privclass is optional
dAmn.demote("chat:devart","rubot","privclass") 		# privclass is optional
dAmn.promote("chat:devart","rubot","privclass") 	# privclass is optional
dAmn.disconnect()					# it'll automatically reconnnect! use exit() to shutdown

ZAPI::message('info',"some info here")		# see /core/class/ZAPI.class.rb for the message() documentation
						# very similar to dAmnbot message()

ZAPI::tokenize("foo bar bam",1) 		# returns "bar"
ZAPI::tokenize("foo bar bam",1,' ',false)	# returns "bar"
ZAPI::tokenize("foo bar bam",1,' ',true)	# returns "bar bam"
ZAPI::tokenize("foo_bar+bam",0,'+') 		# returns "foo_bar"

ZAPI::unformatns("#devart")			# returns "chat:devart"
ZAPI::unformatns("chat:devart")			# returns "#devart"
ZAPI::makens("devart")				# returns "chat:devart"

ZAPI::backtrace("\n")				# returns a string with a backtrace in it

comment standard.txt

COMMENTING METHODS:

# Method description.
#
# param1:: (datatype)
#   Parameter 1 description and purpose.
#
# param2:: (datatype)
#   Parameter 2 description and purpose.
#
#
# Throws: (Exception)
#   The condition that causes the method to throw the above execption.
#
# Returns: (datatype [or datatype...]) 
#   Description of what conditions (pre and post) cause each type
#   of value to be returned.
#
# Example:
# 
#   [...]
#
#    baz = myMethod(:foo,1234)
#
#   [...]
#
def myMethod( param1, param2 )
end

Creating plugins.txt

####
# Developing Plugins
##

Plugins obviously go in the plugins folder, each plugin should be in its own folder with the
folder name being the name of the plugin, in all lower case. There must be at least one file
in the plugin directory and it must be named cmd.rb, That file must define a class with the
name of the folder, following ruby naming conventions so of course the first letter must
be a capital.

Here's a simple template for plugins:

module Plugin
	class Myplugin
		def pc_handleEvent( cmd )
		end
	end
end

All plugins must be in the plugin module, and must define the method pc_handleEvent. The
pc_handleEvent method is called when no other suitable method can be found, its the default.

All plugin entry points (pc_* methods) must accept one parameter, this will be the PluginCommand
object generated from the packet.

Adding a subcommand is easy, just define a new method with the method name prefix pc_, for
example pc_dosomething( cmd ).

Plugins may specify an initialize method that accepts one parameter which will be a
reference to the PluginHandler object. You may define this method with no parameters as well.
Any more than 1 parameter and the plugin will NOT load!!!

Plugins that are threaded MUST be thread safe, else you'll have issues with shared resource
access.

Plugins can also define a few special properties:

switch_prefix: (String)
	This should be a string, it allows all subcommands to be prefixed with a special character
	so that they do no conflict with plugins that use the default method to do something.
	
	For example !define might use the '-' switch_prefix so that you have !define -add,
	!define -remove, !define -update. This would allow the user to do something like
	'!define remove' and not call the remove method by accident.
	
use_thread: (Boolean)
	This allows you to override the default setting that controls if plugin command should
	be threaded or not. True makes the plugin threaded, while false makes it run in the root
	thread.
	
	Careful with this! If the plugin is running in the root thread then it will block on
	recieves. So make sure not to disable threaded on a plugin that will take a long while.
	
	I've set the default inside the bot to always thread plugins, and I've not had any
	significant issues.
	
	One reason you might not want to thread a plugin is if it accesses a shared resource,
	as threaded plugins must be thread safe.

More on this later... I promise!

rubot callbacks.txt

Title: DAmnLib::DAmnClient Callbacks Help File
Date: June 26, 2006
Author: Elliott H. Sprehn

The file contains documentation on the DAmnLib::DAmnClient class's event
system. Each event has a callback hook that allows the developer
to attach listeners that perform tasks when these specific states occur.

For instance you can write a module that runs a specific task every time
the client recieves a kick packet.

DESCRIPTION
----------

Callbacks are in the format "name:( param1, param2 )". 

It should be noted that callbacks propogate up the stack of events, not down it.
For example if a pkt_recv_admin_users event is raised the event order would look
like the following:

	1. pkt_recv_admin_users
	2. pkt_recv_admin
	3. pkt_recv
	4. pkt
	5. tick
	
	Such that the most specific callback is raised first and the least specific
	callback is raised last.
	
The tick event is special and is raised on each iteration of the handler
loop, so all DAmnClient event stacks end in a tick event.

All packet events end with a pkt event.

PARAMETERS
----------

dAmn (DAmnLib::DAmnClient)
	A reference to the DAmnClient object where the connection to
	dAmn and all incoming and outgoing data is handled before
	being passed to the DAmnSock.
	
packet (DAmnLib::DAmnPacket)
	A reference to the DAmnPacket object that was created when the
	packet was read out of the socket. The type of DAmnPacket
	depends on the type of packet that dAmn sent.

CALLBACKS
----------

tick:( dAmn )
pkt:( dAmn, packet )
	pkt_dAmnServer:( dAmn, packet )
	pkt_login:( dAmn, packet )
		pkt_login_good:( dAmn, packet )
		pkt_login_failure:( dAmn, packet )
	pkt_kicked:( dAmn, packet )
		pkt_kicked_user:( dAmn, packet )
		pkt_kicked_badmsg:( dAmn, packet)
	pkt_property:( dAmn, packet )
		pkt_property_privclasses:( dAmn, packet )
		pkt_property_members:( dAmn, packet )
		pkt_property_topic:( dAmn, packet )
		pkt_property_title:( dAmn, packet )
		pkt_property_info:( dAmn, packet )
	pkt_recv:( dAmn, packet )
		pkt_recv_msg:( dAmn, packet )
		pkt_recv_action:( dAmn, packet )
		pkt_recv_join:( dAmn, packet )
		pkt_recv_part:( dAmn, packet )
		pkt_recv_kicked:( dAmn, packet )
		pkt_recv_privchg:( dAmn, packet )
		pkt_recv_admin:( dAmn, packet )
			pkt_recv_admin_privclass:( dAmn, packet )
			pkt_recv_admin_users:( dAmn, packet )
	pkt_join:( dAmn, packet )
	pkt_part:( dAmn, packet )
	pkt_ping:( dAmn, packet )
	pkt_shutdown:( dAmn, packet )
	pkt_disconnect:( dAmn, packet )
	pkt_send:( dAmn, packet )
	
Personal tools