diff --git a/.irpg.conf b/.irpg.conf
index 39073d8e4dad261b465c4b1b76d26402a822734b..76d5f508fb564749e430dc9023f411d030fb2b3f 100644
--- a/.irpg.conf
+++ b/.irpg.conf
@@ -26,16 +26,16 @@
 server irc.iiens.net:6667
 
 # bot's nickname
-botnick IRPG_BOT_DEV
+botnick IRPG_BOT
 
 # bot's username
-botuser IRPG DEV
+botuser IRPG
 
 # real name field
-botrlnm Idle RPG Bot DEV
+botrlnm Idle RPG Bot
 
 # channel name (followed by key, if your channel uses a key
-botchan #idlerpg-dev
+botchan #idlerpg
 
 # additionnal chans where you want the bot to be listening
 # enter as many as you like
@@ -67,7 +67,7 @@ playerurl https://gennuso.iiens.net/irpg/playerview.php?player=
 
 # URL to send users to for help
 #helpurl http://idlerpg.net/
-helpurl https://gennuso.iiens.net/irpg/tldr.php
+helpurl https://gennuso.iiens.net/irpg/
 
 # admin commands list (for admin help)
 admincommurl https://gennuso.iiens.net/irpg/admincomms.txt
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c05c876bf09067ca56cb01ac28115deb5631d4c..77f5a37c104bcaf1033bc614e0284d254325f84c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,9 @@
 This is the changelog for the Idle RPG bot by jotun, jotun@idlerpg.net,
 http://idlerpg.net.
 
-Since v3.2, all changes are from ElTata if not told otherwise
+Since v3.2, all changes are from ElTata
+(eltata@firemail.cc, https://gennuso.iiens.net/irpg/)
+if not told otherwise.
 
 Entries are written backwards. That is, items at the bottom
 of the file were added first, and each subsequent addition is placed on a line
@@ -11,6 +13,15 @@ Thanks for your interest in the Idle RPG! Feel free to contact me with ideas and
 comments, or post them in the issues of the project for public view.
 
 
+---
+## v4.0: released 9/30/19
+
+- huge refactor
+- one game element is one module (quest, fight, event, ...)
+- commands system
+- some commands became available in public
+- removed some features (ban on url, jump servers, ...)
+
 ---
 ## v3.2: released 9/20/19
 
diff --git a/Irpg/Admin.pm b/Irpg/Admin.pm
index aac310bf42cf5814846d382b7dbf06d9734cc491..708ef108b4f951a2b404e327f6b34b8a50845374 100644
--- a/Irpg/Admin.pm
+++ b/Irpg/Admin.pm
@@ -124,7 +124,7 @@ sub do_hog {
 
 sub rehash {
 	my ($userhost, $usernick, $username, $source, @arg) = @_;
-	readconfig();
+	$opts = readconfig();
 	Irpg::Irc::privmsg("Reread config file.",$usernick,1);
 	$opts->{botchan} =~ s/ .*//; # strip channel key if present
 }
@@ -220,21 +220,58 @@ sub clearq {
 
 
 our $commands = {
-	go 		=> {ref => \&join_chans, adm => 1, prv => 1, pub => 0},
-	leave 	=> {ref => \&leave_chans,adm => 1, prv => 1, pub => 0},
-	delold 	=> {ref => \&delold,	 adm => 1, prv => 1, pub => 0},
-	del		=> {ref => \&delacct,	 adm => 1, prv => 1, pub => 0},
-	mkadmin => {ref => \&mkadmin,	 adm => 1, prv => 1, pub => 0},
-	rmadmin => {ref => \&rmadmin,	 adm => 1, prv => 1, pub => 0},
-	hog		=> {ref => \&do_hog,	 adm => 1, prv => 1, pub => 0},
-	rehash	=> {ref => \&rehash,	 adm => 1, prv => 1, pub => 0},
-	chpass	=> {ref => \&chpass,	 adm => 1, prv => 1, pub => 0},
-	chuser	=> {ref => \&chuser,	 adm => 1, prv => 1, pub => 0},
-	push	=> {ref => \&pusch,		 adm => 1, prv => 1, pub => 0},
-	die		=> {ref => \&bot_die,	 adm => 1, prv => 1, pub => 0},
-	restart => {ref => \&restart,	 adm => 1, prv => 1, pub => 0},
-	backup	=> {ref => \&do_backup,	 adm => 1, prv => 1, pub => 0},
-	clearq	=> {ref => \&clearq,	 adm => 1, prv => 1, pub => 0}
+	go 		=> {ref => \&join_chans, adm => 1, prv => 1, pub => 0,
+				hlp => 'GO <chan>[ <chan> ...] : join the given chan(s)'},
+
+	leave 	=> {ref => \&leave_chans,adm => 1, prv => 1, pub => 0,
+				hlp => 'LEAVE <chan>[ <chan> ...] : join the given chan(s)'},
+
+	delold 	=> {ref => \&delold,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'DELOLD <nday> : remove all non-logged-in accounts '.
+					   'inactive in the last <nday> days'},
+
+	del		=> {ref => \&delacct,	 adm => 1, prv => 1, pub => 0,
+				hlp => ''},
+
+	mkadmin => {ref => \&mkadmin,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'MKADMIN <username> : set the isadmin flag '.
+					   'for a given <username>'},
+
+	rmadmin => {ref => \&rmadmin,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'MKADMIN <username> : remove the isadmin flag '.
+					   'for a given <username>'},
+
+	hog		=> {ref => \&do_hog,	 adm => 1, prv => 1, pub => 1,
+				hlp => 'HOG : summon the Hand Of God spell.'},
+
+	rehash	=> {ref => \&rehash,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'REHASH : reload configuration file.'},
+
+	chpass	=> {ref => \&chpass,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'CHPASS <char name> <new password> :'.
+					   'change a character\'s password.'},
+
+	chuser	=> {ref => \&chuser,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'CHUSER <char name> <new char name> :'.
+					   'change a character\'s name.'},
+
+	push	=> {ref => \&pusch,		 adm => 1, prv => 1, pub => 0,
+				hlp => 'PUSH <char name> <seconds> : '.
+					   'remove <seconds> to a player\'s TTL. '.
+				   	   'Can be a negative number to add TTL.'},
+
+	die		=> {ref => \&bot_die,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'DIE : kills the bot.'},
+
+	restart => {ref => \&restart,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'RESTART : restarts the bot.'},
+
+	backup	=> {ref => \&do_backup,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'BACKUP : make a backup copy of the dbfile.'},
+
+	clearq	=> {ref => \&clearq,	 adm => 1, prv => 1, pub => 0,
+				hlp => 'CLEARQ : clear the outgoing message queue. '.
+					   'Useful if the bot is flooded (and plans to respond). '}
 };
 
 1;
diff --git a/Irpg/Main.pm b/Irpg/Main.pm
index 6d5dcda6380e0bdea78bff02c1b70b86c4aabae3..69a64402bff60903fb68b3764a677686afeb7601 100644
--- a/Irpg/Main.pm
+++ b/Irpg/Main.pm
@@ -524,6 +524,30 @@ sub parse {
     }
 }
 
+sub help {
+	my ($userhost, $usernick, $username, $source, @arg) = @_;
+	my $ctxt = $source =~ m/^#/ ? 'pub' : 'prv';
+	my $ret = '';
+	if (!exists($arg[0])) {
+		$ret = "Available commands : ".
+			join(', ', grep({ $commands{$_}->{$ctxt} } keys(%commands))).
+			". Do $opts->{token}help <cmds> for help on a specific command. ".
+			"For information on the game, see $opts->{helpurl}. ";
+	} 
+	elsif (exists($commands{$arg[0]})) {
+		$ret = $commands{$arg[0]}->{hlp};
+	}
+	else {
+		$ret = "No such command '$arg[0]'.";
+	}
+
+	if ($ctxt eq 'prv' && ha($usernick) && !exists($arg[0])) {
+		$ret .= "Admin commands URL is $opts->{admincommurl}";
+	}
+	Irpg::Irc::privmsg($ret, $source);
+}
+
+
 my ($k, $v);
 while (($k,$v) = each %$Irpg::Quest::commands) {$commands{$k} = $v;}
 while (($k,$v) = each %$Irpg::Fight::commands) {$commands{$k} = $v;}
@@ -533,8 +557,18 @@ while (($k,$v) = each %$Irpg::Users::commands) {$commands{$k} = $v;}
 undef $k;
 undef $v;
 
-$commands{pause}  = {ref => \&spause_mode, adm => 1, prv => 1, pub => 0};
-$commands{silent} = {ref => \&silent_mode, adm => 1, prv => 1, pub => 0};
-$commands{reloaddb} = {ref => \&reloaddb, adm => 1, prv => 1, pub => 0};
+$commands{pause} = {ref => \&spause_mode, adm => 1, prv => 1, pub => 0,
+					 hlp => 'PAUSE : toggle pause mode'};
+
+$commands{silent} = {ref => \&silent_mode, adm => 1, prv => 1, pub => 0,
+					 hlp => 'SILENT 0..3 : switch between 4 modes of silence'};
+
+$commands{reloaddb} = {ref => \&reloaddb, adm => 1, prv => 1, pub => 0,
+					   hlp => 'RELOADDB : force bot to reload db file, rewriting memory.'.
+				   			  'Only in pause mode.'};
+
+$commands{help} = {ref => \&help, adm => 0, prv => 1, pub => 1,
+				   hlp => 'HELP [<cmd>] : get list of commands, '.
+				   		  'or help on specific command.'};
 
 1;
diff --git a/Irpg/Quest.pm b/Irpg/Quest.pm
index 8032a6295e3e6cea333e67313fe4cf7952306409..1eb3515ef2bee71cd9909d59e08b28da5a6df16a 100644
--- a/Irpg/Quest.pm
+++ b/Irpg/Quest.pm
@@ -272,7 +272,8 @@ sub quest_info {
 
 
 our $commands = {
-	quest => {ref => \&quest_info, adm => 0, prv => 1, pub => 1}
+	quest => {ref => \&quest_info, adm => 0, prv => 1, pub => 1,
+			  hlp => 'QUEST : Show status of the current quest.'}
 };
 
 1;
diff --git a/Irpg/Users.pm b/Irpg/Users.pm
index 9251ed2f5441278634ae44a39c8a265ef598b012..7f26b3cb17c6c34e2f3382aedb3577dfddd73703 100644
--- a/Irpg/Users.pm
+++ b/Irpg/Users.pm
@@ -131,23 +131,17 @@ sub register {
 
 sub chclass {
 	my ($userhost, $usernick, $username, $source, @arg) = @_;
-	if ($#arg < 2) {
-		Irpg::Irc::privmsg("Try: CHCLASS <char name> <passwd> <new char class>",
-				$usernick, 1);
-	}
-	elsif (!exists($rps->{$arg[0]})) {
-		Irpg::Irc::privmsg("No such username $arg[0].", $usernick, 1);
+	if (!defined($username)) {
+		Irpg::Irc::notice("You are not logged in.", $usernick)
 	}
-	elsif ($rps->{$arg[0]}{pass} ne
-		   crypt($arg[1],$rps->{$arg[0]}{pass})) {
-		Irpg::Irc::notice("Wrong password.", $usernick);
+	elsif (!defined($arg[0])) {
+		Irpg::Irc::notice("Try: CHCLASS <new class>", $usernick);
 	}
 	else {
-		$rps->{$arg[0]}{class} = "@arg[2..$#arg]";
-		Irpg::Irc::privmsg("Class for $arg[0] changed to @arg[2..$#arg].",
-				$usernick, 1);
+		$rps->{$username}{class} = "@arg[2..$#arg]";
+		Irpg::Irc::privmsg("Your class was changed.",$source);
 		Irpg::Irc::chanmsg("Class for $arg[0] changed to @arg[2..$#arg].");
-		penalize($username,"chclass");
+		Irpg::Main::penalize($username,"chclass");
 	}
 }
 
@@ -206,8 +200,7 @@ sub logout {
 sub status {
 	return unless ($opts->{statuscmd});
 	my ($userhost, $usernick, $username, $source, @arg) = @_;
-	my $asked = $arg[0];
-	$asked = $username unless ($asked);
+	my $asked = exists($arg[0]) ? $arg[0] : $username;
 	my $asked_found = Irpg::Main::finduser($asked) unless (exists($rps->{$asked}));
 	$asked = $asked_found if ($asked_found);
 	$asked = Irpg::Main::finduser($asked, 1) unless ($asked_found);
@@ -278,7 +271,7 @@ sub gender {
 		Irpg::Irc::notice("You are not logged in.", $usernick)
 	}
 	elsif (!defined($arg[0]) || $arg[0] =~ /[^MFN]/) {
-		Irpg::Irc::notice("Try: GENDER <M|F|N>",$usernick);
+		Irpg::Irc::notice("Try: GENDER <F[emale]|N[eutral]|M[ale]>",$usernick);
 	}
 	else {
 		$rps->{$username}{gender} = substr(lc($arg[0]),0,1);
@@ -302,19 +295,6 @@ sub rmplayer {
 	}
 }
 
-sub help {
-	my ($userhost, $usernick, $username, $source, @arg) = @_;
-	if ($source =~ /^#/ || !Irpg::Main::ha($usernick)) {
-		Irpg::Irc::privmsg("For information on IRPG bot commands, see ".
-				$opts->{helpurl}, $source);
-	}
-	else {
-		Irpg::Irc::privmsg("Help URL is $opts->{helpurl}", $usernick, 1);
-		Irpg::Irc::privmsg("Admin commands URL is $opts->{admincommurl}",
-				$usernick, 1);
-	}
-}
-
 sub info {
 	my ($userhost, $usernick, $username, $source, @arg) = @_;
 	my $info;
@@ -354,18 +334,44 @@ sub info {
 }
 
 our $commands = {
-	register => {ref => \&register,	adm => 0, prv => 1, pub => 0},
-	removeme => {ref => \&rmplayer,	adm => 0, prv => 1, pub => 1},
-	login	 => {ref => \&login,	adm => 0, prv => 1, pub => 0},
-	logout	 => {ref => \&logout,	adm => 0, prv => 1, pub => 0},
-	newpass	 => {ref => \&newpass,	adm => 0, prv => 1, pub => 0},
-	chclass	 => {ref => \&chclass,	adm => 0, prv => 1, pub => 0},
-	align	 => {ref => \&align,	adm => 0, prv => 1, pub => 1},
-	gender	 => {ref => \&gender,	adm => 0, prv => 1, pub => 1},
-	status	 => {ref => \&status,	adm => 0, prv => 1, pub => 1},
-	whoami	 => {ref => \&whoami,	adm => 0, prv => 1, pub => 1},
-	help	 => {ref => \&help,		adm => 0, prv => 1, pub => 1},
-	info	 => {ref => \&info,		adm => 0, prv => 1, pub => 0}
+	register => {ref => \&register,	adm => 0, prv => 1, pub => 0,
+				 hlp => 'REGISTER <char name> <password <char class> :'.
+			 			'register a new player. <char name> <= 16 chars, '.
+						'<password> <= 8 chars, <char class> <= 30 chars.'},
+
+	removeme => {ref => \&rmplayer,	adm => 0, prv => 1, pub => 1,
+				 hlp => 'REMOVEME : delete your account.'},
+
+	login	 => {ref => \&login,	adm => 0, prv => 1, pub => 0,
+				 hlp => 'LOGIN <char name> <password> : '.
+			 			'login as a character.'},
+
+	logout	 => {ref => \&logout,	adm => 0, prv => 1, pub => 0,
+				 hlp => 'LOGOUT <char name> <password> : '.
+			 			'logout from a character.'},
+
+	newpass	 => {ref => \&newpass,	adm => 0, prv => 1, pub => 0,
+				 hlp => 'NEWPASS <new password> : change password.'},
+
+	chclass	 => {ref => \&chclass,	adm => 0, prv => 1, pub => 0,
+				 hlp => 'CHCLASS <new class> : change class.'},
+
+	align	 => {ref => \&align,	adm => 0, prv => 1, pub => 1,
+				 hlp => 'ALIGN <good|neutral|evil> : change alignment.'},
+
+	gender	 => {ref => \&gender,	adm => 0, prv => 1, pub => 1,
+				 hlp => 'GENDER <F|N|M> : change your gender.'},
+
+	status	 => {ref => \&status,	adm => 0, prv => 1, pub => 1,
+				 hlp => 'STATUS [<usernick>|<char name>] : '.
+			 			'get the status of a character, or of a '.
+						'usernick\'s character. Yours by default.'},
+
+	whoami	 => {ref => \&whoami,	adm => 0, prv => 1, pub => 1,
+				 hlp => 'WHOAMI : check your current status'},
+
+	info	 => {ref => \&info,		adm => 0, prv => 1, pub => 0,
+				 hlp => 'INFO : get some info about the bot.'}
 };
 
 1;
diff --git a/Irpg/Utils.pm b/Irpg/Utils.pm
index cb4d53f183bf1498e61efcca9747814c2861bb4b..e17ed80a608dc8d086be9eab3c6f8266e2fffecf 100644
--- a/Irpg/Utils.pm
+++ b/Irpg/Utils.pm
@@ -29,7 +29,7 @@ our %EXPORT_TAGS = (text=>[qw(&duration &pronoun &mksalt)],
 					data=>[qw(&readconfig &createdb &loaddb &backup &writedb)]);
 
 
-my $_configfile = '.irpg.conf';
+my $_configfile = '.irpg.conf.dev';
 
 my $opts;
 my $rps;
diff --git a/irpg_bot.pl b/irpg_bot.pl
index d5ea7074d2f67cfd6ad3f9ef7e9460c43a3a0703..3e82cdd5480382260e972e1b6e40e8d54febf6a1 100644
--- a/irpg_bot.pl
+++ b/irpg_bot.pl
@@ -1,4 +1,6 @@
 #!/usr/bin/perl
+# irpg bot v4.0.0 by ElTata, eltata@firemail.cc, See https://gennuso.iiens.net/irpg/
+# Based on the  
 # irpg bot v3.1.2 by jotun, jotun@idlerpg.net, et al. See http://idlerpg.net/
 #
 # Some code within this file was written by authors other than myself. As such,
@@ -35,9 +37,8 @@ use Irpg::Utils qw(:DEFAULT &daemonize &set_debug_status :data);
 use Irpg::Main;
 
 my $opts = readconfig();
-#Irpg::Utils::init_pkg(\%opts);
 
-my $version = "3.2.0";
+my $version = "4.0.0";
 
 sub help { # print help message
     (my $prog = $0) =~ s/^.*\///;
@@ -58,8 +59,6 @@ usage: $prog [OPTIONS]
   --botghostcmd, -g    Specify command to send to server to regain primary
                        nickname when in use
   --doban              Advertisement ban on/off flag
-  --okurl, -k          Bot will not ban for web addresses that contain these
-                       strings
   --debug              Debug on/off flag
   --helpurl            URL to refer new users to
   --admincommurl       URL to refer admins to