zetabot 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +281 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/Zeta.gemspec +74 -0
- data/bin/console +14 -0
- data/bin/setup +13 -0
- data/bin/zeta +9 -0
- data/bin/zeta-setup +13 -0
- data/lib/Zeta.rb +13 -0
- data/lib/Zeta/access.rb +84 -0
- data/lib/Zeta/admin.rb +10 -0
- data/lib/Zeta/admin/autojoin.rb +25 -0
- data/lib/Zeta/admin/bot.rb +43 -0
- data/lib/Zeta/admin/channels.rb +36 -0
- data/lib/Zeta/admin/eval.rb +43 -0
- data/lib/Zeta/admin/fifo.rb +45 -0
- data/lib/Zeta/admin/ignore.rb +112 -0
- data/lib/Zeta/admin/oper.rb +50 -0
- data/lib/Zeta/admin/plugins.rb +109 -0
- data/lib/Zeta/admin/users.rb +5 -0
- data/lib/Zeta/blacklist.rb +25 -0
- data/lib/Zeta/cache.rb +0 -0
- data/lib/Zeta/cinch.rb +35 -0
- data/lib/Zeta/config.rb +42 -0
- data/lib/Zeta/gems.rb +0 -0
- data/lib/Zeta/locale.rb +4 -0
- data/lib/Zeta/log.rb +2 -0
- data/lib/Zeta/models.rb +5 -0
- data/lib/Zeta/models/channel.rb +3 -0
- data/lib/Zeta/models/plugin.rb +3 -0
- data/lib/Zeta/models/user.rb +3 -0
- data/lib/Zeta/plugins.rb +27 -0
- data/lib/Zeta/plugins/attack.rb +61 -0
- data/lib/Zeta/plugins/botinfo.rb +83 -0
- data/lib/Zeta/plugins/darkscience.rb +215 -0
- data/lib/Zeta/plugins/dbz.rb +31 -0
- data/lib/Zeta/plugins/dcc.rb +25 -0
- data/lib/Zeta/plugins/dnsbl.rb +36 -0
- data/lib/Zeta/plugins/echo.rb +15 -0
- data/lib/Zeta/plugins/eightball.rb +53 -0
- data/lib/Zeta/plugins/fml.rb +35 -0
- data/lib/Zeta/plugins/fnord.rb +329 -0
- data/lib/Zeta/plugins/gem.rb +0 -0
- data/lib/Zeta/plugins/gif.rb +73 -0
- data/lib/Zeta/plugins/help.rb +32 -0
- data/lib/Zeta/plugins/libsecure.rb +34 -0
- data/lib/Zeta/plugins/macros.rb +124 -0
- data/lib/Zeta/plugins/movie.rb +67 -0
- data/lib/Zeta/plugins/pdfinfo.rb +69 -0
- data/lib/Zeta/plugins/rainbow.rb +65 -0
- data/lib/Zeta/plugins/russian_roulette.rb +90 -0
- data/lib/Zeta/plugins/seen.rb +77 -0
- data/lib/Zeta/plugins/silly.rb +183 -0
- data/lib/Zeta/plugins/snooper.rb +146 -0
- data/lib/Zeta/plugins/urban.rb +60 -0
- data/lib/Zeta/plugins/weather.rb +203 -0
- data/lib/Zeta/plugins/whois.rb +25 -0
- data/lib/Zeta/plugins/wiki.rb +75 -0
- data/lib/Zeta/plugins/wolfram.rb +46 -0
- data/lib/Zeta/tasks/db.rb +0 -0
- data/lib/Zeta/version.rb +3 -0
- data/lib/generators/config/config.rb +0 -0
- data/lib/generators/plugin/new_plugin.rb +0 -0
- data/locale/en/8ball.yml +0 -0
- data/locale/en/attack.yml +148 -0
- data/locale/en/dbz.yml +10 -0
- data/locale/en/ircop.yml +5 -0
- data/locale/en/macros.yml +98 -0
- data/locale/en/meme.yml +27 -0
- metadata +636 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
# This is considered a dangerous plugin, use it only if you know what you are doing
|
2
|
+
module Admin
|
3
|
+
class Oper
|
4
|
+
include Cinch::Plugin
|
5
|
+
include Cinch::Helpers
|
6
|
+
|
7
|
+
enable_acl(:oper)
|
8
|
+
|
9
|
+
# Regex
|
10
|
+
match 'operup', method: :oper_up
|
11
|
+
match /kill ([\S]+) (.+)/, method: :oper_kill
|
12
|
+
match /clearchan (.+) (true|yes)/, method: :oper_clearchan
|
13
|
+
|
14
|
+
# Methods
|
15
|
+
def oper_up(m)
|
16
|
+
if Config.oper_username && Config.oper_password
|
17
|
+
@bot.oper(Config.oper_password, Config.oper_username)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def oper_kill(m, nick, message)
|
22
|
+
return if User(nick).oper?
|
23
|
+
if @bot.irc.send("KILL #{nick} #{message}")
|
24
|
+
m.reply "#{nick}: has been killed by #{m.user.nick} for #{message}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def oper_clearchan(m, chan, confirm=false)
|
29
|
+
if confirm == 'yes' || confirm == 'true'
|
30
|
+
Channel(chan).join
|
31
|
+
User('OperServ').send("mode #{chan} +o #{Zeta}")
|
32
|
+
User('OperServ').send("mode #{chan} +mi")
|
33
|
+
Channel(chan).send('This channel is being cleared')
|
34
|
+
|
35
|
+
number_killed = 0
|
36
|
+
Channel(chan).users.each_key do |u|
|
37
|
+
if u != Zeta
|
38
|
+
number_killed += 1
|
39
|
+
@bot.irc.send("KILL #{u.nick} This channel is being cleared")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
m.safe_reply "#{chan} has been cleared, #{number_killed} clients killed"
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Bot.config.plugins.plugins.push Admin::Oper
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Admin
|
2
|
+
class Plugins
|
3
|
+
include Cinch::Plugin
|
4
|
+
include Cinch::Helpers
|
5
|
+
|
6
|
+
enable_acl(:admin, true)
|
7
|
+
|
8
|
+
set(
|
9
|
+
plugin_name: 'PluginsAdmin',
|
10
|
+
help: "Bot administrator-only private commands.\nUsage: `~join [channel]`; `~part [channel] <reason>`; `~quit [reason]`;",
|
11
|
+
# prefix: /^\?/
|
12
|
+
)
|
13
|
+
|
14
|
+
# Regex
|
15
|
+
match(/plugin load (\S+)(?: (\S+))?/, method: :load_plugin)
|
16
|
+
match(/plugin unload (\S+)/, method: :unload_plugin)
|
17
|
+
match(/plugin reload (\S+)(?: (\S+))?/, method: :reload_plugin)
|
18
|
+
match(/plugin set (\S+) (\S+) (.+)$/, method: :set_option)
|
19
|
+
|
20
|
+
# Methods
|
21
|
+
def load_plugin(m, plugin, mapping)
|
22
|
+
mapping ||= plugin.gsub(/(.)([A-Z])/) { |_|
|
23
|
+
$1 + "_" + $2
|
24
|
+
}.downcase # we downcase here to also catch the first letter
|
25
|
+
|
26
|
+
#file_name = "lib/plugins/#{mapping}.rb"
|
27
|
+
stdlib_plugin = File.join('Zeta', 'plugins', p.to_s)
|
28
|
+
custom_plugin = File.join(Dir.home, '.zeta', 'plugins', p.to_s, "#{p.to_s}.rb")
|
29
|
+
|
30
|
+
unless File.exist?(custom_plugin)
|
31
|
+
m.reply "Could not load #{plugin} because #{file_name} does not exist."
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
load(custom_plugin)
|
37
|
+
rescue
|
38
|
+
m.reply "Could not load #{plugin}."
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
|
42
|
+
begin
|
43
|
+
const = Plugins.const_get(plugin)
|
44
|
+
rescue NameError
|
45
|
+
m.reply "Could not load #{plugin} because no matching class was found."
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
@bot.plugins.register_plugin(const)
|
50
|
+
m.reply "Successfully loaded #{plugin}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def unload_plugin(m, plugin)
|
54
|
+
begin
|
55
|
+
plugin_class = Plugins.const_get(plugin)
|
56
|
+
rescue NameError
|
57
|
+
m.reply "Could not unload #{plugin} because no matching class was found."
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
@bot.plugins.select { |p| p.class == plugin_class }.each do |p|
|
62
|
+
@bot.plugins.unregister_plugin(p)
|
63
|
+
end
|
64
|
+
|
65
|
+
## FIXME not doing this at the moment because it'll break
|
66
|
+
## plugin options. This means, however, that reloading a
|
67
|
+
## plugin is relatively dirty: old methods will not be removed
|
68
|
+
## but only overwritten by new ones. You will also not be able
|
69
|
+
## to change a classes superclass this way.
|
70
|
+
# Cinch::Plugins.__send__(:remove_const, plugin)
|
71
|
+
|
72
|
+
# Because we're not completely removing the plugin class,
|
73
|
+
# reset everything to the starting values.
|
74
|
+
plugin_class.hooks.clear
|
75
|
+
plugin_class.matchers.clear
|
76
|
+
plugin_class.listeners.clear
|
77
|
+
plugin_class.timers.clear
|
78
|
+
plugin_class.ctcps.clear
|
79
|
+
plugin_class.react_on = :message
|
80
|
+
plugin_class.plugin_name = nil
|
81
|
+
plugin_class.help = nil
|
82
|
+
plugin_class.prefix = nil
|
83
|
+
plugin_class.suffix = nil
|
84
|
+
plugin_class.required_options.clear
|
85
|
+
|
86
|
+
m.reply "Successfully unloaded #{plugin}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def reload_plugin(m, plugin, mapping)
|
90
|
+
unload_plugin(m, plugin)
|
91
|
+
load_plugin(m, plugin, mapping)
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_option(m, plugin, option, value)
|
95
|
+
begin
|
96
|
+
const = Plugins.const_get(plugin)
|
97
|
+
rescue NameError
|
98
|
+
m.reply "Could not set plugin option for #{plugin} because no matching class was found."
|
99
|
+
return
|
100
|
+
end
|
101
|
+
@bot.config.plugins.options[const][option.to_sym] = eval(value)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# AutoLoad
|
109
|
+
Bot.config.plugins.plugins.push Admin::Plugins
|
@@ -0,0 +1,25 @@
|
|
1
|
+
BlackListStruct = Struct.new :channels, :users, :plugins
|
2
|
+
|
3
|
+
# Load Cached
|
4
|
+
if File.exists?(File.join(Dir.home, '.zeta', 'cache', 'blacklist.rb'))
|
5
|
+
File.open(File.join(Dir.home, '.zeta', 'cache', 'blacklist.rb')) do |file|
|
6
|
+
Blacklist = Marshal.load(file)
|
7
|
+
end
|
8
|
+
else
|
9
|
+
Blacklist = BlackListStruct.new([], [], [])
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
## Methods
|
14
|
+
def save_blacklist()
|
15
|
+
File.open(File.join(Dir.home, '.zeta', 'cache', 'blacklist.rb'), 'w+') do |file|
|
16
|
+
Marshal.dump(Blacklist, file)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear_blacklist()
|
21
|
+
Blacklist.users = []
|
22
|
+
Blacklist.plugins = []
|
23
|
+
Blacklist.channels = []
|
24
|
+
File.delete(File.join(Dir.home, '.zeta', 'cache', 'blacklist.rb'))
|
25
|
+
end
|
data/lib/Zeta/cache.rb
ADDED
File without changes
|
data/lib/Zeta/cinch.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cinch'
|
2
|
+
|
3
|
+
Bot = Cinch::Bot.new do
|
4
|
+
configure do |c|
|
5
|
+
c.nick = Config.nickname
|
6
|
+
c.nicks = Config.nicks.split(',')
|
7
|
+
c.user = Config.username
|
8
|
+
c.realname = Config.realname
|
9
|
+
c.sasl.username = Config.sasl_username
|
10
|
+
c.sasl.password = Config.sasl_password
|
11
|
+
c.server = Config.server
|
12
|
+
c.password = Config.password
|
13
|
+
c.port = Config.port
|
14
|
+
c.ssl.use = Config.ssl
|
15
|
+
c.max_messages = Config.max_messages
|
16
|
+
c.messages_per_second = Config.messages_per_second
|
17
|
+
c.modes = Config.modes.split(',')
|
18
|
+
c.channels = Config.channels.split(',')
|
19
|
+
c.plugins.prefix = /^#{Config.prefix}/
|
20
|
+
end
|
21
|
+
|
22
|
+
# Execute on confirmation of connection
|
23
|
+
on :connect do
|
24
|
+
# Gain operator privileges if oper username and password are set in config
|
25
|
+
if Config.oper_username && Config.oper_password
|
26
|
+
if !Config.oper_password.empty?
|
27
|
+
@bot.oper(Config.oper_password, Config.oper_username)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
|
data/lib/Zeta/config.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
ConfigStruct = Struct.new(
|
2
|
+
:locale,
|
3
|
+
:nickname,
|
4
|
+
:nicks,
|
5
|
+
:username,
|
6
|
+
:realname,
|
7
|
+
:prefix,
|
8
|
+
:server,
|
9
|
+
:password,
|
10
|
+
:port,
|
11
|
+
:ssl,
|
12
|
+
:sasl_username,
|
13
|
+
:sasl_password,
|
14
|
+
:max_messages,
|
15
|
+
:messages_per_second,
|
16
|
+
:modes,
|
17
|
+
:channels,
|
18
|
+
:custom_plugins,
|
19
|
+
:plugins,
|
20
|
+
:oper_username,
|
21
|
+
:oper_password,
|
22
|
+
:oper_overide,
|
23
|
+
:log_channel,
|
24
|
+
:services,
|
25
|
+
:secure_mode,
|
26
|
+
:secure_channel,
|
27
|
+
:secure_host,
|
28
|
+
:options,
|
29
|
+
:secrets
|
30
|
+
)
|
31
|
+
# Initialize Config
|
32
|
+
Config = ConfigStruct.new
|
33
|
+
|
34
|
+
if File.exists? File.join(Dir.home, '.zeta', 'config.rb')
|
35
|
+
begin
|
36
|
+
require File.join(Dir.home, '.zeta', 'config.rb')
|
37
|
+
rescue => e
|
38
|
+
puts e
|
39
|
+
abort('Unable to read config')
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/Zeta/gems.rb
ADDED
File without changes
|
data/lib/Zeta/locale.rb
ADDED
data/lib/Zeta/log.rb
ADDED
data/lib/Zeta/models.rb
ADDED
data/lib/Zeta/plugins.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Plugins; end
|
2
|
+
|
3
|
+
# Load STDLib plugins
|
4
|
+
if Config.plugins.class == Array
|
5
|
+
Config.plugins.each do |p|
|
6
|
+
begin
|
7
|
+
require File.join('Zeta', 'plugins', p.to_s)
|
8
|
+
rescue => e
|
9
|
+
puts e
|
10
|
+
warn "## Unable to load plugin: #{p} ##"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
## Load Custom Plugins
|
17
|
+
if Config.custom_plugins.class == Array
|
18
|
+
Config.custom_plugins.each do |p|
|
19
|
+
begin
|
20
|
+
require File.join(Dir.home, '.zeta', 'plugins', p.to_s, "#{p.to_s}.rb")
|
21
|
+
rescue => e
|
22
|
+
puts e
|
23
|
+
warn "## Unable to load Custom plugin: #{p} ##"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Plugins
|
6
|
+
class Attack
|
7
|
+
include Cinch::Plugin
|
8
|
+
include Cinch::Helpers
|
9
|
+
|
10
|
+
enable_acl
|
11
|
+
|
12
|
+
set(
|
13
|
+
plugin_name: 'Random Attacker',
|
14
|
+
help: 'Attacks a user with a random attack.\nUsage: `?attack <nick or phrase>`; `<nick or phrase>` may be omitted for a random attack on a random nick.',
|
15
|
+
react_on: :channel
|
16
|
+
)
|
17
|
+
|
18
|
+
def initialize(*args)
|
19
|
+
@attackdict = []
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
# Regex
|
24
|
+
match /attack(?: (.+))?/, group: :attack
|
25
|
+
|
26
|
+
# Methods
|
27
|
+
def execute(m, target=nil)
|
28
|
+
target = target.rstrip
|
29
|
+
target = m.user.nick if !target.nil? && target.match(/(\bmy\b|\b#{@bot.nick}\S*\b|\b\S*self\b)/i)
|
30
|
+
target.gsub(/(\bmy\b|\b#{@bot.nick}\S*\b|\b\S*self\b)/i,m.user.nick+"'s") if !target.nil?;
|
31
|
+
populate_attacks!
|
32
|
+
output = if target
|
33
|
+
attack!(target.gsub(/\x03([0-9]{2}(,[0-9]{2})?)?/,''), m.user.nick)
|
34
|
+
else
|
35
|
+
random_attack!(m.channel.users, m.user.nick)
|
36
|
+
end
|
37
|
+
m.channel.action output
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def populate_attacks!
|
42
|
+
@attackdict.replace load_locale('attack').map {|e| e.gsub(/<(\w+)>/, "%<#{'\1'.downcase}>s") }
|
43
|
+
end
|
44
|
+
|
45
|
+
def grab_random_nick(users)
|
46
|
+
users.to_a.sample[0].nick;
|
47
|
+
end
|
48
|
+
|
49
|
+
def attack!(target, assailant)
|
50
|
+
return nil if target.nil?;
|
51
|
+
@attackdict.sample % {:target => target, :assailant => assailant, :bot => @bot.nick};
|
52
|
+
end
|
53
|
+
|
54
|
+
def random_attack!(targets, assailant)
|
55
|
+
@attackdict.sample % {:target => grab_random_nick(targets), :assailant => assailant, :bot => @bot.nick};
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# AutoLoad
|
61
|
+
Bot.config.plugins.plugins.push Plugins::Attack
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tag_formatter'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'active_support/core_ext/array/conversions'
|
5
|
+
require 'chronic_duration'
|
6
|
+
|
7
|
+
|
8
|
+
module Plugins
|
9
|
+
class BotInfo
|
10
|
+
include Cinch::Plugin
|
11
|
+
include Cinch::Helpers
|
12
|
+
|
13
|
+
enable_acl
|
14
|
+
|
15
|
+
set(
|
16
|
+
plugin_name: "Botinfo",
|
17
|
+
help: "Notices you information about me.\nUsage: `/msg <nick> info`\nUsage: `/msg <nick> list plugins`",
|
18
|
+
react_on: :private)
|
19
|
+
|
20
|
+
def initialize *args
|
21
|
+
super
|
22
|
+
@started_at = Time.now
|
23
|
+
end
|
24
|
+
|
25
|
+
# How to config:
|
26
|
+
# :template -- a path to a textual file (such as *.txt) with fields in it.
|
27
|
+
# :owner -- What to display for the "owner_name" field.
|
28
|
+
# All fields in the text file must be surrounded by '<>', and lines can be commented out using '#'.
|
29
|
+
|
30
|
+
# Regex
|
31
|
+
match 'info', use_prefix: false
|
32
|
+
match 'list plugins', method: :execute_list, use_prefix: false
|
33
|
+
match /^help (.+)$/i, method: :execute_help, use_prefix: false
|
34
|
+
|
35
|
+
# Methods
|
36
|
+
def execute(m)
|
37
|
+
tags = {
|
38
|
+
bot_name: @bot.nick,
|
39
|
+
cinch_version: Cinch::VERSION,
|
40
|
+
#is_admin: config[:admins].is_admin?(m.user.mask) ? "an admin" : "not an admin",
|
41
|
+
owner_name: 'Liothen',
|
42
|
+
plugins_count_remaining: @bot.plugins.length - 10,
|
43
|
+
plugins_head: @bot.plugins[0..9].map {|p| p.class.plugin_name }.join(", "),
|
44
|
+
ruby_platform: RUBY_PLATFORM,
|
45
|
+
ruby_release_date: RUBY_RELEASE_DATE,
|
46
|
+
ruby_version: RUBY_VERSION,
|
47
|
+
session_start_date: @started_at.strftime("%A, %B %e, %Y, at %l:%M:%S %P"),
|
48
|
+
total_channels: @bot.channels.length,
|
49
|
+
total_users: proc {
|
50
|
+
users = [];
|
51
|
+
@bot.channels.each {|c|
|
52
|
+
c.users.each {|u| users << u[0].nick
|
53
|
+
}
|
54
|
+
};
|
55
|
+
users.uniq.size
|
56
|
+
}.call,
|
57
|
+
uptime: ChronicDuration.output(Time.now.to_i - @started_at.to_i)
|
58
|
+
}
|
59
|
+
|
60
|
+
tf = TagFormatter.new open(File.join(__dir__, '../botinfo.txt'),&:read), tags: tags
|
61
|
+
|
62
|
+
m.user.notice tf.parse
|
63
|
+
end
|
64
|
+
|
65
|
+
def execute_list(m)
|
66
|
+
|
67
|
+
list = []
|
68
|
+
@bot.plugins.each {|p| list << p.class.plugin_name };
|
69
|
+
m.user.notice("All #{list.size} currently loaded plugins for #{@bot.nick}:\n#{list.to_sentence}.\nTo view help for a plugin, use `/msg #{@bot.nick} help <plugin name>`.")
|
70
|
+
end
|
71
|
+
|
72
|
+
def execute_help(m, name)
|
73
|
+
list = {}
|
74
|
+
@bot.plugins.each {|p| list[p.class.plugin_name.downcase] = {name: p.class.plugin_name, help: p.class.help} };
|
75
|
+
return m.user.notice("Help for \"#{name}\" could not be found.") if !list.has_key?(name.downcase)
|
76
|
+
m.user.notice("Help for #{Format(:bold,list[name.downcase][:name])}:\n#{list[name.downcase][:help]}")
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# AutoLoad
|
83
|
+
Bot.config.plugins.plugins.push Plugins::BotInfo
|