Compare commits
128 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3a3fd6f190 | ||
|
bec6b9436b | ||
|
7946dcc9cf | ||
|
9826602375 | ||
|
4480cabf5a | ||
|
c588216486 | ||
|
de6f2d82a1 | ||
|
375118ed09 | ||
|
e07d60d03d | ||
|
36f38abcf0 | ||
|
b4c3f0d398 | ||
|
180ea02a76 | ||
|
9b0a7fe99f | ||
|
44750ae719 | ||
|
8419a2d383 | ||
|
415b3c2eb7 | ||
|
5f6fa860a5 | ||
|
a07a30c858 | ||
|
45fe7b5a07 | ||
|
4f49c528af | ||
|
efa044b10d | ||
|
ea9dc759e9 | ||
|
6466b590ab | ||
|
5a8e5f8594 | ||
|
3cca4168a1 | ||
|
479f24af6a | ||
|
ff1b1a94b1 | ||
|
3299e661b2 | ||
|
37624c27c0 | ||
|
59e5b813b9 | ||
|
44e98fb7e9 | ||
|
567458a86e | ||
|
744714dc14 | ||
|
544a936457 | ||
|
e16c9b9c65 | ||
|
2167740ad7 | ||
|
d3a4885f6d | ||
|
85993f6ac3 | ||
|
cd52ea7077 | ||
|
bc2a7e9aeb | ||
|
819dfd2520 | ||
|
57e9e3783e | ||
|
a8947da88b | ||
|
d41a70caea | ||
|
d3fa02c609 | ||
|
c95614cf94 | ||
|
e6d200cd33 | ||
|
c7c6c219b0 | ||
|
d7b71bbdf2 | ||
|
e515d7487d | ||
|
24b520d702 | ||
|
864f066081 | ||
|
3d5e33c638 | ||
|
c402c728e0 | ||
|
8cef10a045 | ||
|
272c16304f | ||
|
bdd71a0971 | ||
|
8560f933ac | ||
|
f690182021 | ||
|
8b7d2722c1 | ||
|
3b410c3fd0 | ||
|
012e5d8648 | ||
|
b6b6dc98a9 | ||
|
8d390c276a | ||
|
54fe5cb83a | ||
|
44d96f2dff | ||
|
4bcfa800ee | ||
|
ea38305df9 | ||
|
55fa5c6ae0 | ||
|
116ebbf60b | ||
|
2b83d225a2 | ||
|
064b09fd76 | ||
|
19621c9962 | ||
|
5bae3bcf42 | ||
|
12ad77ce94 | ||
|
ee3374c594 | ||
|
c4c227ef2c | ||
|
6f6eed76db | ||
|
91223a4b9d | ||
|
bda19f2f83 | ||
|
f30b368f53 | ||
|
37fae8341d | ||
|
d4027cd421 | ||
|
d7c6a41c22 | ||
|
fd603c378d | ||
|
c005f42197 | ||
|
2e8f9f4983 | ||
|
9e7c70a6d6 | ||
|
63b4850b11 | ||
|
ba97c47b93 | ||
|
b574b55192 | ||
|
78b17a113e | ||
|
4766c8ad48 | ||
|
7c55d6d2f8 | ||
|
42b5289362 | ||
|
20416120a8 | ||
|
07139eab6f | ||
|
b7558d6398 | ||
|
5d8b1eb8c0 | ||
|
3e5092c3dc | ||
|
8b8ddc3e12 | ||
|
e0d6a9a98f | ||
|
1ba35ed726 | ||
|
87235cdb1b | ||
|
db1425c242 | ||
|
d4f9cd3176 | ||
|
629d5d71f8 | ||
|
60df899d5a | ||
|
86344c5c7c | ||
|
1c37dc9777 | ||
|
0d36f01c72 | ||
|
e738a3b1bf | ||
|
3e126547f6 | ||
|
a4abe96862 | ||
|
783a5b7856 | ||
|
a08d071493 | ||
|
6b4d5816e4 | ||
|
18b6a2139c | ||
|
8ff39e9a1c | ||
|
3c658d0662 | ||
|
66357d9e7d | ||
|
f2ecfea3c4 | ||
|
2d6e368f22 | ||
|
67ec98ec9d | ||
|
fb6cdf35e8 | ||
|
77b55cfd55 | ||
|
6e16daae66 | ||
|
f9bd99f9bd |
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -1,5 +1,10 @@
|
|||
*.swp
|
||||
*~
|
||||
.log
|
||||
plugins/logger_user.yml
|
||||
old
|
||||
*.log
|
||||
*.sqlite3
|
||||
|
||||
old/
|
||||
|
||||
modules_config.yml
|
||||
plugins/log_user.yml
|
||||
|
||||
|
|
78
DATABASE_EXTENSION.md
Normal file
78
DATABASE_EXTENSION.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Database Extension
|
||||
|
||||
## Plugin Database Extension
|
||||
|
||||
You can configure a database to store a large amount of volatiles informations,
|
||||
like the users rights, etc.
|
||||
To do it, there is an extension, ready to be used.
|
||||
|
||||
### 1. configure the database access
|
||||
|
||||
for exemple, in the ``modules_config.yml``:
|
||||
```yaml
|
||||
plugin:
|
||||
database:
|
||||
adapter: postgres
|
||||
host: localhost
|
||||
port: 5432
|
||||
user: root
|
||||
password: toor
|
||||
database: botpop_db
|
||||
```
|
||||
|
||||
*note: you can also configure it in a specific database file.
|
||||
In these case, adapt the following code.*
|
||||
|
||||
Then, in you plugin, add the following code:
|
||||
|
||||
```ruby
|
||||
class Plugin < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
include Botpop::Plugin::Database # include the extension
|
||||
|
||||
...
|
||||
if ENABLED
|
||||
DB_CONFIG = self.db_config = config(safe: true)['database']
|
||||
DB = self.db_connect!
|
||||
require_relative 'plugin/model' # if you have a model, include it now
|
||||
end
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
### 2. create the database and tables
|
||||
It can be done via 2 ways:
|
||||
|
||||
- migrations: **recommanded**.
|
||||
This is safer and more reliable.
|
||||
There is an example in the plugin [iamalive](plugins/iamalive/).
|
||||
Checkout the documentation of the orm:
|
||||
[sequel migrations](http://sequel.jeremyevans.net/rdoc/files/doc/migration_rdoc.html).
|
||||
- manual: **NOT recommanded**.
|
||||
Create the database and tables manually.
|
||||
|
||||
### 3. use it
|
||||
|
||||
You can access to the database via the constant ``DB``
|
||||
|
||||
```ruby
|
||||
class Plugin ...
|
||||
...
|
||||
def search_word m, word
|
||||
found = DB[:words].where(word: word).first
|
||||
m.reply found ? found[:id] : 'no such word'
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 4. models
|
||||
|
||||
If you want to use models, don't forget to set the "dataset"
|
||||
(association with the right database / table)
|
||||
to avoid conflicts:
|
||||
|
||||
```ruby
|
||||
class Model < Sequel::Model
|
||||
set_dataset DB[:admins]
|
||||
end
|
||||
```
|
24
Gemfile
24
Gemfile
|
@ -1,7 +1,29 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'net-ping'
|
||||
#irc
|
||||
gem 'cinch'
|
||||
|
||||
# scrap yt
|
||||
gem 'mechanize'
|
||||
|
||||
# debug
|
||||
gem 'pry'
|
||||
gem 'colorize'
|
||||
|
||||
#network
|
||||
gem 'net-ping'
|
||||
|
||||
#proxy
|
||||
gem 'htauth'
|
||||
|
||||
#iamalive
|
||||
gem 'sequel'
|
||||
# gem 'sqlite3'
|
||||
gem 'pg'
|
||||
|
||||
#encrypt
|
||||
gem 'tor257'
|
||||
|
||||
#other
|
||||
gem 'nomorebeer'
|
||||
gem 'i18n'
|
||||
|
|
48
Gemfile.lock
48
Gemfile.lock
|
@ -1,19 +1,47 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
cinch (2.2.6)
|
||||
cinch (2.2.7)
|
||||
coderay (1.1.0)
|
||||
colorize (0.7.7)
|
||||
highline (1.7.2)
|
||||
htauth (1.2.0)
|
||||
highline (~> 1.6)
|
||||
domain_name (0.5.25)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
htauth (2.0.0)
|
||||
http-cookie (1.0.2)
|
||||
domain_name (~> 0.5)
|
||||
i18n (0.7.0)
|
||||
mechanize (2.7.3)
|
||||
domain_name (~> 0.5, >= 0.5.1)
|
||||
http-cookie (~> 1.0)
|
||||
mime-types (~> 2.0)
|
||||
net-http-digest_auth (~> 1.1, >= 1.1.1)
|
||||
net-http-persistent (~> 2.5, >= 2.5.2)
|
||||
nokogiri (~> 1.4)
|
||||
ntlm-http (~> 0.1, >= 0.1.1)
|
||||
webrobots (>= 0.0.9, < 0.2)
|
||||
method_source (0.8.2)
|
||||
net-ping (1.7.7)
|
||||
pry (0.10.1)
|
||||
mime-types (2.6.2)
|
||||
mini_portile (0.6.2)
|
||||
net-http-digest_auth (1.4)
|
||||
net-http-persistent (2.9.4)
|
||||
net-ping (1.7.8)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
nomorebeer (1.1)
|
||||
ntlm-http (0.1.1)
|
||||
pg (0.18.3)
|
||||
pry (0.10.3)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
sequel (4.27.0)
|
||||
slop (3.6.0)
|
||||
tor257 (0.2)
|
||||
nomorebeer (~> 1.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.1)
|
||||
webrobots (0.1.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -22,8 +50,14 @@ DEPENDENCIES
|
|||
cinch
|
||||
colorize
|
||||
htauth
|
||||
i18n
|
||||
mechanize
|
||||
net-ping
|
||||
nomorebeer
|
||||
pg
|
||||
pry
|
||||
sequel
|
||||
tor257
|
||||
|
||||
BUNDLED WITH
|
||||
1.10.5
|
||||
1.11.2
|
||||
|
|
98
README.md
98
README.md
|
@ -1,21 +1,30 @@
|
|||
# botpop
|
||||
[![Code Climate](https://codeclimate.com/github/pouleta/botpop/badges/gpa.svg)](https://codeclimate.com/github/pouleta/botpop)
|
||||
[![Code Climate](https://codeclimate.com/github/Nephos/botpop/badges/gpa.svg)](https://codeclimate.com/github/Nephos/botpop)
|
||||
[![GitHub version](https://badge.fury.io/gh/Nephos%2Fbotpop.svg)](http://badge.fury.io/gh/Nephos%2Fbotpop)
|
||||
|
||||
## Requirements
|
||||
|
||||
- Ruby 2.0 or greater
|
||||
- Postgresql 9.3 or greater
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
## Installation
|
||||
Ruby 2 or greater is required. To be compatible with Ruby 1.9, you can try :
|
||||
|
||||
```bash
|
||||
sed 's/prepend/include/g' -i botpop.rb
|
||||
```
|
||||
but i did never try... You better update ruby ! ;)
|
||||
|
||||
Too install the stuff, just do :
|
||||
```bash
|
||||
bundle install
|
||||
bundle install # install the required gems
|
||||
cp modules_config.yml.example modules_config.yml
|
||||
editor modules_config.yml # set the database settings, etc.
|
||||
# create your database
|
||||
rake db:install # migrate the base plugin
|
||||
```
|
||||
|
||||
to install the gems.
|
||||
|
||||
|
||||
## Arguments
|
||||
By default, only the first occurence of the argument will be used, unless specified.
|
||||
|
@ -51,13 +60,19 @@ end
|
|||
Some official plugins are developped. You can propose your own creation by pull request, or add snippets link to the wiki.
|
||||
|
||||
## List
|
||||
- [Base](https://github.com/pouleta/botpop/blob/master/plugins/base.rb) : this is a basic plugin, providing __version, code, help, and troll__
|
||||
- [Network](https://github.com/pouleta/botpop/blob/master/plugins/network.rb) : an usefull plugin with commands __ping, ping ip, ping http, traceroute, dos attack and poke__
|
||||
- [Searchable](https://github.com/pouleta/botpop/blob/master/plugins/searchable.rb) : a little plugin providing irc research with engines like __google, wikipedia, ruby-doc, etc...__
|
||||
x- [Coupon](https://github.com/pouleta/botpop/blob/master/plugins/coupons.rb) : the original aim of the bot. It get coupons for the challenge __pathwar__
|
||||
x- [Intranet](https://github.com/pouleta/botpop/blob/master/plugins/intranet.rb) : an useless plugin to check the intranet of epitech
|
||||
- [Proxy](https://github.com/pouleta/botpop/blob/master/plugins/proxy.rb) : an audacious plugin to create user access to a local proxy
|
||||
- [Log](https://github.com/pouleta/botpop/blob/master/plugins/log.rb) : simple logger
|
||||
- [Base](https://github.com/Nephos/botpop/blob/master/plugins/base.rb) : this is a basic plugin, providing __version, code, help, and troll__. It also provide a full groups's system.
|
||||
- [Network](https://github.com/Nephos/botpop/blob/master/plugins/network.rb) : an usefull plugin with commands __ping, ping ip, ping http, traceroute, dos attack and poke__
|
||||
- [Searchable](https://github.com/Nephos/botpop/blob/master/plugins/searchable.rb) : a little plugin providing irc research with engines like __google, wikipedia, ruby-doc, etc...__
|
||||
- [Proxy](https://github.com/Nephos/botpop/blob/master/plugins/proxy.rb) : an audacious plugin to create user access to a local proxy
|
||||
- [Log](https://github.com/Nephos/botpop/blob/master/plugins/log.rb) : simple logger
|
||||
- [IAmAlive](https://github.com/Nephos/botpop/tree/master/plugins/iamalive) : a plugin to learn how to respond to the users. Fucking machine learning, oh yearh.
|
||||
- [CeQueTuDisNaAucunSens](https://github.com/Nephos/botpop/tree/master/plugins/cequetudisnaaucunsens.rb) : a funny plugin to say "ce que tu dis n'a aucun sens" without any meaning.
|
||||
- [Points](https://github.com/Nephos/botpop/tree/master/plugins/points.rb) : a gem to add points to an user. ``!p noob for_you``
|
||||
- [Anecdote](https://github.com/Nephos/botpop/blob/master/plugins/anecdote.rb) : a cool meme generator plugin with nazi and youtuber. French meme.
|
||||
|
||||
### In version 0.X, not upgraded to v1
|
||||
- [Coupon](https://github.com/Nephos/botpop/blob/master/plugins/coupons.rb) : the original aim of the bot. It get coupons for the challenge __pathwar__
|
||||
- [Intranet](https://github.com/Nephos/botpop/blob/master/plugins/intranet.rb) : an useless plugin to check the intranet of epitech
|
||||
|
||||
|
||||
## Create your own
|
||||
|
@ -68,15 +83,15 @@ You should take the time to read the documentation before developping anything.
|
|||
|
||||
|
||||
### Example of new plugin
|
||||
A full example of plugin code is provided in the commented file : [Example of Fury Plugin](https://github.com/pouleta/botpop/blob/master/plugins/example.rb)
|
||||
A full example of plugin code is provided in the commented file : [Example of Fury Plugin](https://github.com/Nephos/botpop/blob/master/plugins/example.rb)
|
||||
|
||||
First, put your ruby code file in ``plugins/``, and put your code in the scope :
|
||||
```ruby
|
||||
class MyFuryPlugin < BotpopPlugin
|
||||
class MyFuryPlugin < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
def exec_whatkingofanimal m
|
||||
m.reply "Die you son of a" + ["lion", "pig", "red panda"].shuffle.first + " !!"
|
||||
m.reply "Die you son of a" + ["lion", "pig", "red panda"].sample + " !!"
|
||||
end
|
||||
...code...
|
||||
end
|
||||
|
@ -86,7 +101,7 @@ end
|
|||
### Matching messages
|
||||
To create a matching to respond to a message, you have to specifie in your plugin :
|
||||
```ruby
|
||||
class MyFuryPlugin < BotpopPlugin
|
||||
class MyFuryPlugin < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
match(/!whatkingofanimal.*/, use_prefix: false, method: :exec_whatkingofanimal)
|
||||
...code...
|
||||
|
@ -95,13 +110,13 @@ end
|
|||
|
||||
|
||||
### Add entry to the !help command
|
||||
The __official plugin__ [Base](https://github.com/pouleta/botpop/blob/master/plugins/base.rb) provides the command __!help__ and __!help plugin__.
|
||||
The __official plugin__ [Base](https://github.com/Nephos/botpop/blob/master/plugins/base.rb) provides the command __!help__ and __!help plugin__.
|
||||
|
||||
It list the avaliable commands of the plugins. You can add your help to your plugin by providing a __HELP__ constant.
|
||||
__The strings should be as short as possible.__
|
||||
You should write it like the following:
|
||||
```ruby
|
||||
class MyFuryPlugin < BotpopPlugin
|
||||
class MyFuryPlugin < Botpop::Plugin
|
||||
HELP = ["!whatkingofanimal", "!animallist", "!checkanimal [type]"]
|
||||
...code...
|
||||
end
|
||||
|
@ -110,15 +125,52 @@ end
|
|||
|
||||
### Enable and disable plugin
|
||||
You can enable or disable plugin by using the constant __ENABLED__.
|
||||
It should be linked with the __Botpop::CONFIG__.
|
||||
The constant must be defined by the developper of the plugin.
|
||||
For example, you can implement it like :
|
||||
```ruby
|
||||
class MyFuryPlugin < BotpopPlugin
|
||||
CONFIG = Botpop::CONFIG['myfurry'] || raise(MissingConfigurationZone, 'myfurry')
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
|
||||
class MyFuryPlugin < Botpop::Plugin
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
end
|
||||
```
|
||||
|
||||
Then, a simple line in the ``modules_configuration.yml`` file should be enough.
|
||||
|
||||
|
||||
### Plugin Configuration
|
||||
You can configure your plugins via the file ``modules_configuration.yml``.
|
||||
If you considere that your plugin needs a particular configuration file, then create a new one il the ``plugins`` directory.
|
||||
|
||||
To use the configuration loaded by ``modules_configuration.yml``, use the method ``config``.
|
||||
|
||||
``config`` takes an optionnal Hash as argument. It can take:
|
||||
|
||||
- ``:safe => (true or false)``
|
||||
- ``:name => (string or symbol)``
|
||||
|
||||
This method returns a Hash with configuration.
|
||||
|
||||
By default, the method raise a ``MissingConfigurationZone`` error if no entry in the ``modules_configuration.yml`` file.
|
||||
|
||||
The configuration file ``modules_configuration.yml`` must seems like :
|
||||
```yaml
|
||||
name:
|
||||
entry: "string"
|
||||
entry2:
|
||||
- 1
|
||||
- 2.2
|
||||
- "ohoh"
|
||||
- nextelement:
|
||||
- oh oh !
|
||||
```
|
||||
|
||||
By default, the ``modules_configuration.yml`` file is configured for default plugins.
|
||||
|
||||
### Plugin Database
|
||||
|
||||
Check this specified [README FOR DATABASE IN PLUGINS](DATABASE_EXTENSION.md)
|
||||
|
||||
### Rights managements (users, groups)
|
||||
|
||||
Requires postgresql, because it uses the pg_array extension.
|
||||
|
||||
Check this specified [README FOR RIGHTS MANAGEMENT](RIGHTS_MANAGEMENT.md)
|
||||
|
|
15
RIGHTS_MANAGEMENT.md
Normal file
15
RIGHTS_MANAGEMENT.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# How to use rights ?
|
||||
|
||||
In your plugin, add the method ``cmd_allowed?`` and use it like in the following example:
|
||||
|
||||
```ruby
|
||||
class Plugin < Botpop::Plugin
|
||||
...
|
||||
def cmd_allowed? m
|
||||
return if not Base.cmd_allowed? m, ["groupname"]
|
||||
end
|
||||
|
||||
def exec_some_match m
|
||||
return Base.cmd_allowed? m, ["iaa"]
|
||||
end
|
||||
```
|
16
Rakefile
16
Rakefile
|
@ -1,7 +1,17 @@
|
|||
#encoding: utf-8
|
||||
|
||||
task :default => [:test]
|
||||
require 'yaml'
|
||||
CONFIG = YAML.load_file("modules_config.yml")
|
||||
|
||||
task :test do
|
||||
ruby "test/test.rb"
|
||||
#require 'sequel'
|
||||
#DB_BASE = Sequel.connect(CONFIG['base']['database'])
|
||||
|
||||
#task :default => ["x:x"]
|
||||
|
||||
namespace :db do
|
||||
task :install do
|
||||
# TODO: use CONFIG['base']
|
||||
`sequel -m plugins/base/migrations postgres://root:toor@localhost:5432/botpop_base`
|
||||
end
|
||||
end
|
||||
|
||||
|
|
16
arguments.rb
16
arguments.rb
|
@ -1,8 +1,8 @@
|
|||
# encoding: utf-8
|
||||
class Arguments
|
||||
|
||||
# @arg : name [String] the option to search
|
||||
# @arg : name [Array] the options to search (multiples keys avaliables)
|
||||
# @param name [String] the option to search
|
||||
# @param name [Array] the options to search (multiples keys avaliables)
|
||||
def get_one_argument(name, default_value)
|
||||
if name.is_a? String
|
||||
i = @argv.index(name)
|
||||
|
@ -83,17 +83,13 @@ class Arguments
|
|||
end
|
||||
|
||||
def disable_plugins
|
||||
i = 0
|
||||
plugins = []
|
||||
argv = @argv.dup
|
||||
while i
|
||||
loop do
|
||||
i = argv.index('--plugin-disable')
|
||||
if i
|
||||
plugin = argv[i + 1]
|
||||
plugin = plugin + '.rb' if plugin[-4..-1] != '.rb'
|
||||
plugins << File.expand_path(plugin, plugin_directory)
|
||||
argv = argv[(i+2)..(-1)]
|
||||
end
|
||||
break unless i
|
||||
plugins << @argv[i + 1]
|
||||
argv = argv[(i+2)..(-1)]
|
||||
end
|
||||
return plugins
|
||||
end
|
||||
|
|
25
botpop.rb
25
botpop.rb
|
@ -15,15 +15,18 @@ require 'colorize'
|
|||
require_relative 'arguments'
|
||||
require_relative 'botpop_plugin_inclusion'
|
||||
require_relative 'builtins'
|
||||
|
||||
class BotpopPlugin
|
||||
def config
|
||||
Botpop::CONFIG[self.class.to_s]
|
||||
end
|
||||
end
|
||||
require_relative 'database'
|
||||
|
||||
class Botpop
|
||||
|
||||
class Plugin
|
||||
def self.config(infos={})
|
||||
name = (infos[:name] || self.to_s.downcase).to_s
|
||||
config = Botpop::CONFIG[name]
|
||||
return config || (raise(MissingConfigurationZone, self.to_s) unless infos[:safe])
|
||||
end
|
||||
end
|
||||
|
||||
def self.load_version
|
||||
begin
|
||||
return IO.read('version')
|
||||
|
@ -41,10 +44,14 @@ class Botpop
|
|||
Module.constants.select{ |m|
|
||||
(m = Module.const_get(m) rescue false) and
|
||||
(m.is_a?(Class)) and
|
||||
(m.ancestors.include?(BotpopPlugin)) and
|
||||
(m != BotpopPlugin) and
|
||||
(m.ancestors.include?(Plugin)) and
|
||||
(m.included_modules.include?(Cinch::Plugin))
|
||||
}.map{|m| Module.const_get(m)}
|
||||
}.
|
||||
select{|m| not ARGUMENTS.disable_plugins.include? m.to_s
|
||||
}.
|
||||
map{|m| Module.const_get(m)
|
||||
}.
|
||||
select{|m| m::ENABLED}
|
||||
end
|
||||
|
||||
# FIRST LOAD THE CONFIGURATION
|
||||
|
|
66
builtins.rb
66
builtins.rb
|
@ -1,41 +1,39 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module BotpopBuiltins
|
||||
class Botpop
|
||||
module Builtins
|
||||
|
||||
def self.dos(ip, duration)
|
||||
`timeout #{duration} hping --flood '#{ip}' 2>&1`
|
||||
end
|
||||
|
||||
def self.ping(ip)
|
||||
Net::Ping::External.new(ip).ping?
|
||||
end
|
||||
|
||||
def self.intra_state
|
||||
Net::Ping::External.new("intra.epitech.eu").ping? ? "Intra ok" : "Intra down"
|
||||
end
|
||||
|
||||
def self.trace ip
|
||||
`tracepath '#{ip}'`.to_s.split("\n")
|
||||
end
|
||||
|
||||
def self.get_msg m
|
||||
URI.encode(m.params[1..-1].join(' ').gsub(/\![^ ]+ /, ''))
|
||||
end
|
||||
|
||||
def self.get_ip m
|
||||
m.params[1..-1].join(' ').gsub(/\![^ ]+ /, '').gsub(/[^[:alnum:]\-\_\.]/, '')
|
||||
end
|
||||
|
||||
def self.get_ip_from_nick m
|
||||
nick = get_ip m
|
||||
ip = m.target.users.keys.find{|u| u.nick == nick rescue nil}.host rescue nil
|
||||
return {nick: nick, ip: ip}
|
||||
end
|
||||
|
||||
def self.dos(ip, duration)
|
||||
`timeout #{duration} hping --flood '#{ip}' 2>&1`
|
||||
end
|
||||
|
||||
def self.ping(ip)
|
||||
Net::Ping::External.new(ip).ping?
|
||||
end
|
||||
|
||||
def self.intra_state
|
||||
Net::Ping::External.new("intra.epitech.eu").ping? ? "Intra ok" : "Intra down"
|
||||
end
|
||||
|
||||
def self.trace ip
|
||||
`tracepath '#{ip}'`.to_s.split("\n")
|
||||
end
|
||||
|
||||
def self.get_msg m
|
||||
URI.encode(m.params[1..-1].join(' ').gsub(/\![^ ]+ /, ''))
|
||||
end
|
||||
|
||||
def self.get_ip m
|
||||
m.params[1..-1].join(' ').gsub(/\![^ ]+ /, '').gsub(/[^[:alnum:]\-\_\.]/, '')
|
||||
end
|
||||
|
||||
def self.get_ip_from_nick m
|
||||
nick = get_ip m
|
||||
ip = m.target.users.keys.find{|u| u.nick == nick rescue nil}.host rescue nil
|
||||
return {nick: nick, ip: ip}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module BotpopPlugins
|
||||
prepend BotpopBuiltins
|
||||
end
|
||||
|
||||
class MissingConfigurationZone < RuntimeError
|
||||
|
|
|
@ -3,3 +3,5 @@ moul -- guest
|
|||
ernestjkaufman -- guest
|
||||
FolenScare -- guest
|
||||
JoycePF -- guest
|
||||
Deborah -- guest
|
||||
Shigugu -- guest
|
||||
|
|
21
database.rb
Normal file
21
database.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
#coding: utf-8
|
||||
|
||||
class Botpop
|
||||
class Plugin
|
||||
module Database
|
||||
|
||||
def self.append_features(b)
|
||||
b.extend self
|
||||
end
|
||||
|
||||
attr_accessor :db_config
|
||||
attr_reader :db
|
||||
def db_connect!
|
||||
require 'sequel'
|
||||
@db = Sequel.connect(@db_config)
|
||||
@db
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +1,67 @@
|
|||
base:
|
||||
enable: true
|
||||
help_wait_duration: 120
|
||||
database:
|
||||
adapter: postgres
|
||||
host: localhost
|
||||
port: 5432
|
||||
user: root
|
||||
password: toor
|
||||
database: botpop_base
|
||||
|
||||
dice:
|
||||
enable: true
|
||||
|
||||
eip:
|
||||
enable: true
|
||||
|
||||
rootme:
|
||||
enable: false
|
||||
|
||||
points:
|
||||
enable: true
|
||||
|
||||
anecdote:
|
||||
enable: true
|
||||
|
||||
log:
|
||||
enable: true
|
||||
default_started: false
|
||||
|
||||
encrypt:
|
||||
enable: true
|
||||
|
||||
puppet:
|
||||
enable: true
|
||||
|
||||
taggle:
|
||||
enable: true
|
||||
ntimes: 10
|
||||
wait: 0.3
|
||||
|
||||
youtube:
|
||||
enable: true
|
||||
display: "Youtube: ___TITLE___ ___URL___"
|
||||
reduce_url: https://youtu.be/___ID___
|
||||
search_url: https://www.youtube.com/results?search_query=___MSG___
|
||||
|
||||
poilo:
|
||||
enable: false
|
||||
list:
|
||||
u: cul
|
||||
a: bras
|
||||
o: dos
|
||||
i: kiki
|
||||
eu: lepreux
|
||||
y: nazi
|
||||
oi: petit-pois
|
||||
eau: bébé oiseau
|
||||
au: marmot
|
||||
ou: doudou
|
||||
an: manant
|
||||
|
||||
searchable:
|
||||
yt: https://www.youtube.com/results?search_query=___MSG___
|
||||
#yt: https://www.youtube.com/results?search_query=___MSG___ # use the plugin youtube instead
|
||||
xv: http://www.xvideos.com/?k=___MSG___
|
||||
yp: https://www.youporn.com/search/?query=___MSG___
|
||||
|
||||
|
@ -67,10 +117,20 @@ epitech:
|
|||
enable: false
|
||||
|
||||
iamalive:
|
||||
enable: false
|
||||
database_file: plugins/iamalive.database.yaml
|
||||
enable: true
|
||||
default_mode: live
|
||||
database:
|
||||
adapter: postgres
|
||||
host: localhost
|
||||
port: 5432
|
||||
user: root
|
||||
password: toor
|
||||
database: botpop_iamalive
|
||||
|
||||
say_goodbye:
|
||||
cequetudisnaaucunsens:
|
||||
enabled: true
|
||||
|
||||
saygoodbye:
|
||||
chapui_s:
|
||||
- "@chapui_s t'es là ?"
|
||||
- "chapui_s !!! T'es où ?"
|
||||
|
@ -99,4 +159,3 @@ say_goodbye:
|
|||
- "No râge de mon gradÂge !"
|
||||
poulet_a:
|
||||
- "Nan mais c'est un scandale ! Pourquoi j'ai grade A ?"
|
||||
|
46
plugins/anecdote.rb
Normal file
46
plugins/anecdote.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
require 'nokogiri'
|
||||
require 'net/http'
|
||||
|
||||
class Anecdote < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!a(necdote)? (.+)/, use_prefix: false, method: :exec_new)
|
||||
|
||||
HELP = ["!anecdote <...>"]
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def exec_new m, _, s
|
||||
s.downcase!
|
||||
f = I18n.transliterate(s)[0]
|
||||
x = "Après je vous propose "
|
||||
x += (%w(a e i o u y).include?(f) ? "d'" : "de ") if not s.match(/^(d'|de ).+/)
|
||||
s = x + s
|
||||
url = URI.parse 'http://memegenerator.net/create/instance'
|
||||
post_data = {
|
||||
'imageID' => 14185932,
|
||||
'generatorID' => 5374051,
|
||||
'watermark1' => 1,
|
||||
'uploadtoImgur' => 'true',
|
||||
'text0' => s,
|
||||
'text1' => "Ca fera une petite anecdote !!",
|
||||
}
|
||||
meme = nil
|
||||
begin
|
||||
Net::HTTP.start url.host do |http|
|
||||
post = Net::HTTP::Post.new url.path
|
||||
post.set_form_data post_data
|
||||
res = http.request post
|
||||
location = res['Location']
|
||||
redirect = url + location
|
||||
get = Net::HTTP::Get.new redirect.request_uri
|
||||
res = http.request get
|
||||
doc = Nokogiri.HTML res.body
|
||||
meme = doc.css("meta")[7]['content']
|
||||
end
|
||||
rescue => _err
|
||||
end
|
||||
m.reply meme ? meme : "Achtung ! ACHTUUUUNG !!!"
|
||||
end
|
||||
|
||||
end
|
|
@ -1,18 +1,37 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Base < BotpopPlugin
|
||||
include Cinch::Plugin
|
||||
require "i18n"
|
||||
I18n.config.available_locales = [:en, :fr]
|
||||
|
||||
match /^!troll .+/ , use_prefix: false, method: :exec_troll
|
||||
class Base < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
include Botpop::Plugin::Database
|
||||
|
||||
match(/^!troll .+/ , use_prefix: false, method: :exec_troll)
|
||||
match "!version" , use_prefix: false, method: :exec_version
|
||||
match "!code" , use_prefix: false, method: :exec_code
|
||||
match "!cmds" , use_prefix: false, method: :exec_help
|
||||
match "!help" , use_prefix: false, method: :exec_help
|
||||
match /^!help \w+/ , use_prefix: false, method: :exec_help_plugin
|
||||
match(/^!help \w+/ , use_prefix: false, method: :exec_help_plugin)
|
||||
|
||||
HELP = ["!troll [msg]", "!version", "!code", "!help [plugin]", "!cmds"]
|
||||
CONFIG = Botpop::CONFIG['base'] || raise(MissingConfigurationZone, self.to_s)
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
|
||||
match("!register", use_prefix: false, method: :user_register)
|
||||
match("!user ls", use_prefix: false, method: :user_ls)
|
||||
match(/^!user (\w+) group ls$/, use_prefix: false, method: :user_group_ls)
|
||||
match(/^!user (\w+) group add (\w+)/, use_prefix: false, method: :user_group_add)
|
||||
match(/^!user (\w+) group rm (\w+)/, use_prefix: false, method: :user_group_rm)
|
||||
|
||||
HELP = ["!troll [msg]", "!version", "!code", "!help [plugin]", "!cmds",
|
||||
"!user ls", "!user <name> group <ls|add|rm> [group]"]
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
if ENABLED
|
||||
DB_CONFIG = self.db_config = CONFIG['database']
|
||||
DB = self.db_connect!
|
||||
DB.extension :pg_array
|
||||
require_relative 'base/UserModel'
|
||||
require_relative 'base/user'
|
||||
end
|
||||
|
||||
def help_wait_before_quit
|
||||
HELP_WAIT_DURATION.times do
|
||||
|
@ -24,21 +43,13 @@ class Base < BotpopPlugin
|
|||
def help_get_plugins_str
|
||||
["Plugins found : " + Botpop::PLUGINS.size.to_s] +
|
||||
Botpop::PLUGINS.map do |plugin|
|
||||
plugin.to_s.split(':').last + ': ' + plugin::HELP.join(', ') rescue nil
|
||||
plugin.to_s.split(':').last
|
||||
end.compact
|
||||
end
|
||||
|
||||
HELP_WAIT_DURATION = CONFIG['help_wait_duration'] || 120
|
||||
HELP_WAIT_DURATION = config['help_wait_duration'] || 120
|
||||
def help m
|
||||
@@help_lock ||= Mutex.new
|
||||
if @@help_lock.try_lock
|
||||
@@help_time = 0
|
||||
help_get_plugins_str().each{|str| m.reply str} # display
|
||||
help_wait_before_quit rescue nil
|
||||
@@help_lock.unlock
|
||||
else
|
||||
m.reply "Help already sent #{@@help_time} seconds ago. Wait #{HELP_WAIT_DURATION - @@help_time} seconds more."
|
||||
end
|
||||
m.reply help_get_plugins_str.join(', ')
|
||||
end
|
||||
|
||||
def exec_version m
|
||||
|
@ -46,7 +57,7 @@ class Base < BotpopPlugin
|
|||
end
|
||||
|
||||
def exec_code m
|
||||
m.reply "https://github.com/pouleta/botpop"
|
||||
m.reply "https://github.com/Nephos/botpop"
|
||||
end
|
||||
|
||||
def exec_help m
|
||||
|
@ -54,8 +65,8 @@ class Base < BotpopPlugin
|
|||
end
|
||||
|
||||
def exec_help_plugin m
|
||||
module_name = m.message.split(" ").last
|
||||
i = Botpop::PLUGINS.map{|e| e.to_s.split(":").last}.index(module_name)
|
||||
module_name = m.message.split(" ").last.downcase
|
||||
i = Botpop::PLUGINS.map{|e| e.to_s.split(":").last.downcase}.index(module_name)
|
||||
if i.nil?
|
||||
m.reply "No plugin #{module_name}"
|
||||
return
|
||||
|
@ -66,7 +77,7 @@ class Base < BotpopPlugin
|
|||
|
||||
def exec_troll m
|
||||
# hours = (Time.now.to_i - Time.gm(2015, 04, 27, 9).to_i) / 60 / 60
|
||||
s = BotpopBuiltins.get_msg m
|
||||
s = Botpop::Builtins.get_msg m
|
||||
url = "http://www.fuck-you-internet.com/delivery.php?text=#{s}"
|
||||
m.reply url
|
||||
end
|
||||
|
|
21
plugins/base/UserModel.rb
Normal file
21
plugins/base/UserModel.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require 'sequel'
|
||||
|
||||
class User < Sequel::Model
|
||||
|
||||
def is_admin?
|
||||
self.admin
|
||||
end
|
||||
|
||||
def add_group
|
||||
end
|
||||
|
||||
def del_group
|
||||
end
|
||||
|
||||
def belongs_to? group
|
||||
self.groups.include? group
|
||||
end
|
||||
|
||||
set_dataset Base::DB[:users]
|
||||
|
||||
end
|
10
plugins/base/migrations/01_create_user.rb
Normal file
10
plugins/base/migrations/01_create_user.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:users) do
|
||||
primary_key :id
|
||||
String :name, null: false, unique: true
|
||||
TrueClass :admin
|
||||
column :groups, "text[]"
|
||||
end
|
||||
end
|
||||
end
|
11
plugins/base/migrations/02_create_points.rb
Normal file
11
plugins/base/migrations/02_create_points.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:points) do
|
||||
# primary_key :id
|
||||
String :assigned_to
|
||||
String :assigned_by
|
||||
String :type
|
||||
DateTime :created_at
|
||||
end
|
||||
end
|
||||
end
|
12
plugins/base/migrations/03_create_message.rb
Normal file
12
plugins/base/migrations/03_create_message.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:messages) do
|
||||
primary_key :id
|
||||
String :author
|
||||
String :dest
|
||||
String :content
|
||||
DateTime :created_at
|
||||
DateTime :read_at
|
||||
end
|
||||
end
|
||||
end
|
14
plugins/base/migrations/04_create_emails.rb
Normal file
14
plugins/base/migrations/04_create_emails.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:emails) do
|
||||
primary_key :id
|
||||
String :authname
|
||||
String :address
|
||||
DateTime :created_at
|
||||
Integer :usage
|
||||
Bool :primary, default: false
|
||||
|
||||
index [:address], unique: true
|
||||
end
|
||||
end
|
||||
end
|
14
plugins/base/migrations/05_create_random_sequences.rb
Normal file
14
plugins/base/migrations/05_create_random_sequences.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:random_sentences) do
|
||||
primary_key :id
|
||||
String :author
|
||||
String :trigger
|
||||
String :content
|
||||
Bool :enabled, default: true
|
||||
DateTime :created_at
|
||||
|
||||
index :trigger, unique: true
|
||||
end
|
||||
end
|
||||
end
|
10
plugins/base/migrations/06_create_eip.rb
Normal file
10
plugins/base/migrations/06_create_eip.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:eips) do
|
||||
primary_key :id
|
||||
String :author
|
||||
String :title
|
||||
DateTime :created_at
|
||||
end
|
||||
end
|
||||
end
|
70
plugins/base/user.rb
Normal file
70
plugins/base/user.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
class Base
|
||||
|
||||
def find_and_exec(m, name)
|
||||
u = User.where(name: name).first
|
||||
if u
|
||||
yield u
|
||||
else
|
||||
m.reply "No such user '#{name}'"
|
||||
end
|
||||
end
|
||||
|
||||
def self.cmd_allowed? m, groups=["admin"], verbose=true
|
||||
user = User.where(name: m.user.authname).where("groups @> '{#{groups.join(',')}}'").first
|
||||
if user.nil?
|
||||
m.reply "No authorized" if verbose
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_allowed? m, groups=["admin"], verbose=true
|
||||
Base.cmd_allowed?(m, groups, verbose)
|
||||
end
|
||||
|
||||
def user_register m
|
||||
return m.reply "You are not connected" if m.user.authname.nil?
|
||||
begin
|
||||
admin = (User.count == 0)
|
||||
u = User.create(name: m.user.authname,
|
||||
admin: admin,
|
||||
groups: [admin ? 'admin' : 'default'])
|
||||
m.reply "Welcome ##{u.id} #{u.name}"
|
||||
rescue => _
|
||||
m.reply "Cannot register #{m.user.authname}"
|
||||
end
|
||||
end
|
||||
|
||||
def user_ls m
|
||||
c = User.count
|
||||
m.reply User.limit(20).all.map(&:name).join(', ')
|
||||
if c > 20
|
||||
m.reply "And #{c-20} more"
|
||||
end
|
||||
end
|
||||
|
||||
def user_group_ls m, name
|
||||
cmd_allowed? m
|
||||
find_and_exec(m, name) do |u|
|
||||
m.reply u.groups.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
def user_group_add m, name, group
|
||||
cmd_allowed? m
|
||||
find_and_exec(m, name) do |u|
|
||||
u.update(groups: (u.groups + [group]))
|
||||
m.reply "group #{group} added to #{u.name}"
|
||||
end
|
||||
end
|
||||
|
||||
def user_group_rm m, name, group
|
||||
cmd_allowed? m
|
||||
find_and_exec(m, name) do |u|
|
||||
u.update(groups: (u.groups - [group]))
|
||||
m.reply "group #{group} removed from #{u.name}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
68
plugins/cequetudisnaaucunsens.rb
Normal file
68
plugins/cequetudisnaaucunsens.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
class CeQueTuDisNAAucunSens < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/^[^!].+$/, use_prefix: false, method: :say_random_sentence)
|
||||
match(/^!random_sentence register ([^|]+)\|(.+)/, use_prefix: false, method: :register_trigger)
|
||||
match(/^!random_sentence remove (.+)/, use_prefix: false, method: :remove_trigger)
|
||||
|
||||
HELP = ["!random_sentence register trigger | content",
|
||||
"!random_sentence remove trigger" ]
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def cmd_allowed? m
|
||||
return Base.cmd_allowed? m, ["random_sentence"]
|
||||
end
|
||||
|
||||
def say_random_sentence m
|
||||
trigger = I18n.transliterate(m.message).strip
|
||||
r = Base::DB[:random_sentences].where(enabled: true).where('? ~* "trigger"', trigger).select(:content).first
|
||||
return if r.nil?
|
||||
m.reply r[:content].split(' ').shuffle.join(' ')
|
||||
end
|
||||
|
||||
def say_random m
|
||||
m.reply %w(ce que tu dis n'a aucun sens).shuffle.join(' ')
|
||||
end
|
||||
|
||||
def register_trigger m, t, c
|
||||
return if not cmd_allowed? m
|
||||
t = t.triggerize
|
||||
begin
|
||||
Base::DB[:random_sentences].insert(trigger: t,
|
||||
content: c.strip,
|
||||
author: m.user.authname,
|
||||
created_at: Time.now.utc)
|
||||
m.reply "The trigger \"#{t.strip}\" will raise \"#{c.strip}\""
|
||||
rescue => _err
|
||||
m.reply "Error. Cannot register this trigger"
|
||||
m.reply _err
|
||||
end
|
||||
end
|
||||
|
||||
def remove_trigger m, t
|
||||
return if not cmd_allowed? m
|
||||
t = t.triggerize
|
||||
n = Base::DB[:random_sentences].where(trigger: t).delete
|
||||
m.reply "Deleted #{n} trigger"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class String
|
||||
def triggerize
|
||||
t = self.dup
|
||||
t = I18n.transliterate(t).strip
|
||||
t = Regexp.quote(t)
|
||||
t.gsub!(/((a)a+)/i, '\2') # ... i know :(
|
||||
t.gsub!(/((e)e+)/i, '\2')
|
||||
t.gsub!(/((i)i+)/i, '\2')
|
||||
t.gsub!(/((o)o+)/i, '\2')
|
||||
t.gsub!(/((u)u+)/i, '\2')
|
||||
t.gsub!(/((y)y+)/i, '\2')
|
||||
t.gsub!(/([aeiouy])/, '\1+')
|
||||
# TODO: not only " " but also ponctuation etc.
|
||||
t = "^(.* )?#{t}( .*)?$"
|
||||
return t
|
||||
end
|
||||
end
|
12
plugins/dice/Character.rb
Normal file
12
plugins/dice/Character.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class Character
|
||||
|
||||
attr_accessor :carac, :skills, :hp, :classes, :bab
|
||||
def initialize(str, dex, con, int, wiz, cha, opt={})
|
||||
@carac = {str: str, dex: dex, con: con, int: int, wiz: wiz, cha: cha}
|
||||
@skills = {}
|
||||
@hp = opt[:hp] || nil
|
||||
@classes = opt[:classes] || {}
|
||||
@bab = opt[:bab] || [0]
|
||||
end
|
||||
|
||||
end
|
98
plugins/dice/Dice.rb
Normal file
98
plugins/dice/Dice.rb
Normal file
|
@ -0,0 +1,98 @@
|
|||
class FrozenDice
|
||||
attr_reader :min, :max, :values, :nb, :faces
|
||||
|
||||
def initialize arg
|
||||
if arg.is_a? String
|
||||
v = arg.match(/^(?<nb>\d+)d(?<faces>\d+)$/i)
|
||||
if v
|
||||
set_rolldice v
|
||||
else
|
||||
raise ArgumentError unless arg.match(/^\d+$/)
|
||||
set_value arg.to_i
|
||||
end
|
||||
elsif arg.is_a? Integer
|
||||
set_value arg
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
end
|
||||
|
||||
def throw
|
||||
@nb.times.map{ rand(@values) }
|
||||
end
|
||||
|
||||
def test
|
||||
self.throw.inject(&:+)
|
||||
end
|
||||
|
||||
def mean
|
||||
v = values.to_a
|
||||
if v.size % 2 == 0
|
||||
(v[v.size / 2 - 1] + v[v.size / 2]) * 0.5
|
||||
else
|
||||
v[v.size / 2]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def set_rolldice v
|
||||
@nb, @faces = v[:nb].to_i, v[:faces].to_i
|
||||
@max = @faces
|
||||
@min = 1
|
||||
@values = @min..@max
|
||||
end
|
||||
|
||||
def set_value v
|
||||
@nb = 1
|
||||
@faces = v
|
||||
@min = @faces
|
||||
@max = @faces
|
||||
@values = @min..@max
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Dice
|
||||
attr_accessor :bonus, :dices
|
||||
|
||||
def initialize *arg
|
||||
@dices = []
|
||||
arg.each do |a1|
|
||||
a1.gsub!(" ", "")
|
||||
a1.split(/[+ ]/).each do |a2|
|
||||
@dices << FrozenDice.new(a2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def min
|
||||
@dices.map do |dice|
|
||||
dice.min
|
||||
end
|
||||
end
|
||||
|
||||
def mean
|
||||
@dices.map do |dice|
|
||||
dice.mean
|
||||
end
|
||||
end
|
||||
|
||||
def max
|
||||
@dices.map do |dice|
|
||||
dice.max
|
||||
end
|
||||
end
|
||||
|
||||
def throw
|
||||
@dices.map do |dice|
|
||||
dice.throw
|
||||
end
|
||||
end
|
||||
|
||||
def test
|
||||
@dices.map do |dice|
|
||||
dice.test
|
||||
end.inject(&:+)
|
||||
end
|
||||
|
||||
end
|
12
plugins/dice/Warrior.rb
Normal file
12
plugins/dice/Warrior.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
require_relative 'Character'
|
||||
|
||||
class Warrior < Character
|
||||
|
||||
def initialize str, *arg
|
||||
super(str, 10, 10, 10, 10, 10, *arg)
|
||||
end
|
||||
|
||||
def str; carac[:str]; end
|
||||
def bstr; (str - 10) / 2; end
|
||||
|
||||
end
|
84
plugins/dice/Weapon.rb
Normal file
84
plugins/dice/Weapon.rb
Normal file
|
@ -0,0 +1,84 @@
|
|||
require_relative 'Dice'
|
||||
require_relative 'Warrior'
|
||||
|
||||
class Weapon
|
||||
attr_reader :from, :degats, :opt, :bonus
|
||||
|
||||
def initialize from, degats, opt, bonus, attack_opt={}
|
||||
@from = from
|
||||
@bonus = from.bab.map{|e| e + bonus}
|
||||
@degats = Dice.new(degats + "+#{(from.bstr * 1.5).ceil}")
|
||||
@hands = opt[:hands] || 1
|
||||
@max = attack_opt[:max] || Float::INFINITY
|
||||
end
|
||||
|
||||
def min
|
||||
@degats.min
|
||||
end
|
||||
|
||||
def max
|
||||
@degats.max
|
||||
end
|
||||
|
||||
def mean
|
||||
@degats.mean
|
||||
end
|
||||
|
||||
def test
|
||||
@degats.test
|
||||
end
|
||||
|
||||
def mean_p(ca=20.0)
|
||||
d = @degats.mean.inject(&:+)
|
||||
p(ca).map do |b|
|
||||
(b * d).round(4)
|
||||
end
|
||||
end
|
||||
|
||||
def p(ca=20.0)
|
||||
@bonus.map do |b|
|
||||
((b + from.bstr) / ca.to_f).round(4)
|
||||
end
|
||||
end
|
||||
|
||||
def mean_p_total(ca=20.0)
|
||||
mean_p(ca).inject(&:+).round(4)
|
||||
end
|
||||
|
||||
def to_s(ca=20)
|
||||
"mean: #{mean} * #{p(ca)} => #{mean_p(ca)} = #{mean_p_total(ca)}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
alteration = 2
|
||||
taille = -2
|
||||
bonus = alteration + taille
|
||||
|
||||
epees = []
|
||||
normal = Warrior.new 18, {bab: [7, 1]}
|
||||
epees << ["normal", Weapon.new(normal, "4d6+2", {hands: 2}, bonus)]
|
||||
|
||||
rage = Warrior.new 19+4, {bab: [7, 1]}
|
||||
epees << ["rage", Weapon.new(rage, "4d6+2", {hands: 2}, bonus)]
|
||||
|
||||
fren = Warrior.new 19+6, {bab: [7, 1]}
|
||||
epees << ["frenesie", Weapon.new(fren, "4d6+2", {hands: 2}, bonus)]
|
||||
|
||||
ra_fr = Warrior.new 19+4+6, {bab: [7, 7, 1]}
|
||||
epees << ["rage+frenesie", Weapon.new(ra_fr, "4d6+2", {hands: 2}, bonus)]
|
||||
|
||||
ra_fr_so = Warrior.new 19+6+4, {bab: [7, 7, 1]}
|
||||
epees << ["rage+frenesie+sorciere", Weapon.new(ra_fr_so, "4d6+2+5+1d6", {hands: 2}, bonus)]
|
||||
|
||||
ra_fr_so_buff = Warrior.new 19+6+4+4, {bab: [7, 7, 1]}
|
||||
epees << ["rage+frenesie+sorciere+taureau+benediction", Weapon.new(ra_fr_so, "4d6+2+5+1d6", {hands: 2}, bonus+1)]
|
||||
|
||||
ra_fr_so_buff_char = Warrior.new 19+6+4+4, {bab: [7, 7]}
|
||||
epees << ["rage+frenesie+sorciere+taureau+benediction+charge", Weapon.new(ra_fr_so, "4d6+2+5+1d6", {hands: 2}, bonus+1+2, {max: 2})]
|
||||
|
||||
epees = Hash[epees]
|
||||
require 'pry'
|
||||
binding.pry
|
||||
end
|
22
plugins/diceroller.rb
Normal file
22
plugins/diceroller.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require_relative 'dice/Dice'
|
||||
require_relative 'dice/Weapon'
|
||||
require_relative 'dice/Character'
|
||||
require_relative 'dice/Warrior'
|
||||
|
||||
class Diceroller < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!roll (.+)/, use_prefix: false, method: :exec_roll)
|
||||
|
||||
HELP = ["!roll (d20 ...)"]
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
CHARACTER = Warrior.new 10, {bab: [0]}
|
||||
|
||||
def exec_roll(m, roll)
|
||||
val = Weapon.new(CHARACTER, roll, {hands: 1}, 0).test
|
||||
m.reply "Roll ... '#{roll}' ... #{val}"
|
||||
end
|
||||
|
||||
end
|
35
plugins/eip.rb
Normal file
35
plugins/eip.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
class Eip < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!eip add (.*)/, use_prefix: false, method: :exec_add)
|
||||
match(/!eip ls/, use_prefix: false, method: :exec_ls)
|
||||
match(/!eip (\d+)/, use_prefix: false, method: :exec_id)
|
||||
|
||||
HELP = ["!eip add ...", "!eip ls", "!eip id"]
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def exec_add(m, title)
|
||||
begin
|
||||
Base::DB[:eips].insert(author: m.user.authname,
|
||||
title: title,
|
||||
created_at: Time.now)
|
||||
m.reply "Ok ! #{title} is a new eip"
|
||||
rescue
|
||||
m.reply "Err"
|
||||
end
|
||||
end
|
||||
|
||||
def exec_id(m, id)
|
||||
all = Base::DB[:eips].where(id: Integer(id)).first
|
||||
m.reply all[:title] rescue m.reply("no such id")
|
||||
end
|
||||
|
||||
def exec_ls(m)
|
||||
all = Base::DB[:eips].limit(3).reverse.all
|
||||
all.each{|e| m.reply e[:title]}
|
||||
n = Base::DB[:eips].count
|
||||
m.reply("There is #{n} elements")
|
||||
end
|
||||
|
||||
end
|
20
plugins/encrypt.rb
Normal file
20
plugins/encrypt.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
require 'tor257/core'
|
||||
require 'base64'
|
||||
|
||||
class Encrypt < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/^!tor257 (c|d) (\w+) (.+)/, use_prefix: false, method: :exec_tor257)
|
||||
|
||||
HELP = ["!tor257 <c|d> keyphrase data"]
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def exec_tor257 m, type, k, d
|
||||
d = Base64.decode64(d.strip) if type == 'd'
|
||||
e = Tor257::Message.new(d).encrypt(Tor257::Key.new(k)).to_s
|
||||
e = Base64.encode64(e) if type == 'c'
|
||||
m.reply e
|
||||
end
|
||||
|
||||
end
|
|
@ -1,14 +1,14 @@
|
|||
class MyFury < BotpopPlugin
|
||||
class MyFury < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!whatkingofanimal.*/, use_prefix: false, method: :exec_whatkingofanimal)
|
||||
|
||||
HELP = ["!whatkingofanimal", "!animallist", "!checkanimal [type]"]
|
||||
CONFIG = Botpop::CONFIG['example'] || raise(MissingConfigurationZone, self.to_s)
|
||||
ENABLED = CONFIG['enable'].nil? ? false : CONFIG['enable']
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def exec_whatkingofanimal m
|
||||
m.reply "Die you son of a" + ["lion", "pig", "red panda"].shuffle.first + " !!"
|
||||
m.reply "Die you son of a" + ["lion", "pig", "red panda"].sample + " !!"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
111
plugins/iamalive.rb
Normal file
111
plugins/iamalive.rb
Normal file
|
@ -0,0 +1,111 @@
|
|||
class IAmAlive < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
include Botpop::Plugin::Database
|
||||
|
||||
match(/^[^!].*/, use_prefix: false, method: :register_entry)
|
||||
match(/^[^!].*/, use_prefix: false, method: :react_on_entry)
|
||||
match(/^!iaa reac(tivity)?$/, use_prefix: false, method: :get_reactivity)
|
||||
match(/^!iaa reac(tivity)? \d{1,3}$/, use_prefix: false, method: :set_reactivity)
|
||||
match(/^!iaa learn$/, use_prefix: false, method: :set_mode_learn)
|
||||
match(/^!iaa live$/, use_prefix: false, method: :set_mode_live)
|
||||
match(/^!iaa mode$/, use_prefix: false, method: :get_mode)
|
||||
match(/^!iaa stats?$/, use_prefix: false, method: :get_stats)
|
||||
match(/^!iaa forget( (\d+ )?(.+))?/, use_prefix: false, method: :forget)
|
||||
match(/^!iaa last( \w+)?$/, use_prefix: false, method: :get_last)
|
||||
|
||||
CONFIG = config(:safe => true)
|
||||
ENABLED = CONFIG['enable'] || false
|
||||
HELP = ["!iaa reac", "!iaa reac P", "!iaa learn", "!iaa live", "!iaa mode",
|
||||
"!iaa stats", "!iaa forget (Nx SENTENCE)", "!iaa last (nick)",
|
||||
"!iaa user [add/remove/list]"]
|
||||
|
||||
@@mode = config['default_mode'].to_sym
|
||||
@@reactivity = config['reactivity'] || 50
|
||||
|
||||
def cmd_allowed? m
|
||||
return Base.cmd_allowed? m, ["iaa"]
|
||||
end
|
||||
|
||||
if ENABLED
|
||||
DB_CONFIG = self.db_config = CONFIG['database']
|
||||
DB = self.db_connect!
|
||||
require_relative 'iamalive/entry'
|
||||
end
|
||||
|
||||
def register_entry m
|
||||
Entry.create(user: (m.user.authname || m.user.nick), message: m.message, channel: m.channel.to_s)
|
||||
forget_older! if rand(1..100) == 100
|
||||
end
|
||||
|
||||
def react_on_entry m
|
||||
return if @@mode != :live
|
||||
e = Entry.where('LOWER(message) = LOWER(?)', m.message).select(:id).all.map(&:id).map{|x| x+1}
|
||||
if @@reactivity > rand(1..100)
|
||||
answer_to(m, e)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def answer_to m, e
|
||||
a = Entry.where(id: e).to_a.sample
|
||||
if not a.nil?
|
||||
sleep(a.message.split.size.to_f / 10)
|
||||
m.reply a.message
|
||||
Entry.create(user: "self", message: a.message, channel: m.channel.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def forget_older!
|
||||
log "Forget the older entry"
|
||||
Entry.first.delete
|
||||
end
|
||||
public
|
||||
|
||||
def get_reactivity m
|
||||
m.reply "Current reactivity: #{@@reactivity}"
|
||||
end
|
||||
|
||||
def set_reactivity m
|
||||
return if not cmd_allowed? m
|
||||
@@reactivity = m.message.split[2].to_i
|
||||
end
|
||||
|
||||
def set_mode_learn m
|
||||
return if not cmd_allowed? m
|
||||
@@mode = :learn
|
||||
end
|
||||
|
||||
def set_mode_live m
|
||||
return if not cmd_allowed? m
|
||||
@@mode = :live
|
||||
end
|
||||
|
||||
def get_mode m
|
||||
m.reply "Current mode: #{@@mode}"
|
||||
end
|
||||
|
||||
def get_stats m
|
||||
m.reply "Registered sentences: #{Entry.count}"
|
||||
end
|
||||
|
||||
def forget m, arguments, nb, what
|
||||
return if not cmd_allowed? m
|
||||
if arguments.nil?
|
||||
last = Entry.where(channel: m.channel.to_s, user: "self").last
|
||||
m.reply last ? "\"#{last.message}\" Forgotten" : "Nop"
|
||||
last.delete
|
||||
else
|
||||
nb = nb.to_i if not nb.nil?
|
||||
nb ||= Entry.where(message: what).count
|
||||
n = Entry.where(message: what).order_by(:id).reverse.limit(nb).map(&:delete).size rescue 0
|
||||
m.reply "(#{n}x) \"#{what}\" Forgotten"
|
||||
end
|
||||
end
|
||||
|
||||
def get_last m, user
|
||||
user.strip! if user
|
||||
last = Entry.where(channel: m.channel.to_s, user: (user || "self")).last
|
||||
m.reply "#{user}: #{last ? last.message : 'no message found'}"
|
||||
end
|
||||
|
||||
end
|
29
plugins/iamalive/README.md
Normal file
29
plugins/iamalive/README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# I Am Alive
|
||||
|
||||
I am alive is a plugin, that allows to bot to answer to me :)
|
||||
|
||||
## Initialization
|
||||
|
||||
### Database
|
||||
|
||||
Firstly, create the database and migrate it. To do this, use the following command.
|
||||
In the ``modules_config.yml`` file, configure it for your engine.
|
||||
As it use sequel engine, it is compatible with sqlite, mysql, postgres, etc.
|
||||
Checkout the [sequel documentaiton](http://sequel.jeremyevans.net/documentation.html) for more informations.
|
||||
Then, execute one of two:
|
||||
|
||||
```bash
|
||||
sequel -m plugins/iamalive/migrations sqlite://plugins/iamalive/db.sqlite3
|
||||
sequel -m plugins/iamalive/migrations postgres://root:toor@localhost:5432/botpop_iamalive
|
||||
...
|
||||
```
|
||||
|
||||
You can change the name of the database via the global configuration file (see the example).
|
||||
|
||||
### User / Admin
|
||||
|
||||
Only authorized users have the rights to administrate the iaa plugin.
|
||||
Only when there is no administrator (by default), you can use the command "!iaa user add NICK" to add your NICK to the database.
|
||||
Be sure you have a protected identity.
|
||||
|
||||
Then, only administrators can add / remove admin from the list.
|
9
plugins/iamalive/admin.rb
Normal file
9
plugins/iamalive/admin.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require 'sequel'
|
||||
|
||||
class IAmAlive
|
||||
|
||||
class Admin < Sequel::Model
|
||||
set_dataset DB[:admins]
|
||||
end
|
||||
|
||||
end
|
31
plugins/iamalive/entry.rb
Normal file
31
plugins/iamalive/entry.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
require 'sequel'
|
||||
|
||||
class IAmAlive
|
||||
|
||||
class IAAMessage < String
|
||||
|
||||
def initialize *arg
|
||||
super(*arg)
|
||||
self.strip!
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Entry < Sequel::Model
|
||||
def before_create
|
||||
self.created_at ||= Time.now
|
||||
self.message.strip!
|
||||
self.message_origin = self.message
|
||||
self.message = IAAMessage.new(self.message).to_s
|
||||
super
|
||||
end
|
||||
set_dataset DB[:entries]
|
||||
|
||||
def self.anwser(message)
|
||||
Entry.where('LOWER(message) = LOWER(?)', m.message.to_iaa_message).
|
||||
select(:id).all.map(&:id).map{|x| x+1}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
12
plugins/iamalive/migrations/01_create_entry.rb
Normal file
12
plugins/iamalive/migrations/01_create_entry.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:entries) do
|
||||
primary_key :id
|
||||
String :user, null: false
|
||||
String :channel, null: false
|
||||
String :message, null: false, text: true
|
||||
String :message_origin, null: false, text: true
|
||||
DateTime :created_at
|
||||
end
|
||||
end
|
||||
end
|
8
plugins/iamalive/migrations/02_create_admin.rb
Normal file
8
plugins/iamalive/migrations/02_create_admin.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
Sequel.migration do
|
||||
change do
|
||||
create_table(:admins) do
|
||||
primary_key :id
|
||||
String :user, null: false
|
||||
end
|
||||
end
|
||||
end
|
27
plugins/kick.rb
Normal file
27
plugins/kick.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Kick < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!k (.+)/, use_prefix: false, method: :exec_kick)
|
||||
match(/!kick (.+)/, use_prefix: false, method: :exec_kick)
|
||||
match(/!k ([^|]+)\|(.+)/, use_prefix: false, method: :exec_kick_message)
|
||||
match(/!kick ([^|]+)\|(.+)/, use_prefix: false, method: :exec_kick_message)
|
||||
|
||||
HELP = ["!kick nickname <message>"]
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def exec_kick m, victim
|
||||
len = CONFIG["list"].length - 1
|
||||
msg = CONFIG["list"][rand(0..len)]
|
||||
m.channel.kick(victim, msg)
|
||||
m.reply "Bye bye " + victim
|
||||
end
|
||||
|
||||
def exec_kick_message m, victim, reason
|
||||
m.channel.kick(victim, reason)
|
||||
m.reply "Bye bye " + victim
|
||||
end
|
||||
|
||||
end
|
|
@ -1,24 +1,24 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Log < BotpopPlugin
|
||||
class Log < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match /users/, use_prefix: true, method: :exec_list_user
|
||||
match /remove .+/, use_prefix: true, method: :exec_remove_user
|
||||
match /add .+/, use_prefix: true, method: :exec_add_user
|
||||
match /clean/, use_prefix: true, method: :exec_clean
|
||||
match /status/, use_prefix: true, method: :exec_status
|
||||
match /enable/, use_prefix: true, method: :exec_log_enable
|
||||
match /users$/, use_prefix: true, method: :exec_list_user
|
||||
match /remove .+$/, use_prefix: true, method: :exec_remove_user
|
||||
match /add .+$/, use_prefix: true, method: :exec_add_user
|
||||
match /clean$/, use_prefix: true, method: :exec_clean
|
||||
match /status$/, use_prefix: true, method: :exec_status
|
||||
match /enable$/, use_prefix: true, method: :exec_log_enable
|
||||
match /.+/, use_prefix: false, method: :exec_log
|
||||
|
||||
HELP = ["!log enable", "!log add", "!log remove", "!log users", "!log clean", "!log status"]
|
||||
CONFIG = Botpop::CONFIG['log'] || raise(MissingConfigurationZone, self.to_s)
|
||||
CONFIG = config
|
||||
ENABLED = CONFIG['enable'].nil? ? false : CONFIG['enable']
|
||||
USER_CONFIG = "plugins/log_user.yml"
|
||||
USERS = YAML.load_file(USER_CONFIG) || raise(MissingConfigurationZone, USER_CONFIG)
|
||||
|
||||
@@log_user_list = USERS["list"]
|
||||
@@log_enabled = false
|
||||
@@log_enabled = CONFIG["default_started"]
|
||||
|
||||
def exec_list_user m
|
||||
m.reply @@log_user_list.join(", ")
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
list:
|
||||
- poulet_a
|
|
@ -1,6 +1,6 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Network < BotpopPlugin
|
||||
class Network < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match("!ping", use_prefix: false, method: :exec_ping)
|
||||
|
@ -13,8 +13,8 @@ class Network < BotpopPlugin
|
|||
|
||||
HELP = ["!ping", "!ping [ip]", "!httping [ip]",
|
||||
"!dos [ip]", "!fok [nick]", "!trace [ip]", "!poke [nick]"]
|
||||
CONFIG = Botpop::CONFIG['network'] || raise(MissingConfigurationZone, self.to_s)
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
private
|
||||
# Conversion of the string to value in ms
|
||||
|
@ -23,15 +23,15 @@ class Network < BotpopPlugin
|
|||
end
|
||||
public
|
||||
|
||||
DOS_DURATION = CONFIG['dos_duration'] || '2s'
|
||||
DOS_WAIT_DURATION_STRING = CONFIG['dos_wait'] || '5s'
|
||||
DOS_DURATION = config['dos_duration'] || '2s'
|
||||
DOS_WAIT_DURATION_STRING = config['dos_wait'] || '5s'
|
||||
DOS_WAIT_DURATION = config_string_to_time DOS_WAIT_DURATION_STRING
|
||||
|
||||
# Trace is complexe. 3 functions used exec_trace_display_lines, exec_trace_with_time, exec_trace
|
||||
TRACE_DURATION_INIT_STRING_DEFAULT = "0.3s"
|
||||
TRACE_DURATION_INIT_STRING = CONFIG['trace_duration_init'] || TRACE_DURATION_INIT_STRING_DEFAULT
|
||||
TRACE_DURATION_INIT_STRING = config['trace_duration_init'] || TRACE_DURATION_INIT_STRING_DEFAULT
|
||||
TRACE_DURATION_INCR_STRING_DEFAULT = "0.1s"
|
||||
TRACE_DURATION_INCR_STRING = CONFIG['trace_duration_incr'] || TRACE_DURATION_INCR_STRING_DEFAULT
|
||||
TRACE_DURATION_INCR_STRING = config['trace_duration_incr'] || TRACE_DURATION_INCR_STRING_DEFAULT
|
||||
TRACE_DURATION_INIT = config_string_to_time TRACE_DURATION_INIT_STRING
|
||||
TRACE_DURATION_INCR = config_string_to_time TRACE_DURATION_INCR_STRING
|
||||
|
||||
|
|
51
plugins/ops.rb
Normal file
51
plugins/ops.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Ops < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!op/, use_prefix: false, method: :exec_op)
|
||||
match(/!op (.+)/, use_prefix: false, method: :exec_op_other)
|
||||
match(/!deop/, use_prefix: false, method: :exec_deop)
|
||||
match(/!deop (.+)/, use_prefix: false, method: :exec_deop_other)
|
||||
match(/!v/, use_prefix: false, method: :exec_voice)
|
||||
match(/!v (.+)/, use_prefix: false, method: :exec_voice_other)
|
||||
match(/!dv/, use_prefix: false, method: :exec_devoice)
|
||||
match(/!dv (.+)/, use_prefix: false, method: :exec_devoice_other)
|
||||
|
||||
HELP = ["!op <nickname>", "!deop <nickname>"]
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def exec_op m
|
||||
m.channel.op(m.user)
|
||||
end
|
||||
|
||||
def exec_op_other m, other
|
||||
m.channel.op(other)
|
||||
end
|
||||
|
||||
def exec_deop m
|
||||
m.channel.deop(m.user)
|
||||
end
|
||||
|
||||
def exec_deop_other m, other
|
||||
m.channel.deop(other)
|
||||
end
|
||||
|
||||
def exec_voice m
|
||||
m.channel.voice(m.user)
|
||||
end
|
||||
|
||||
def exec_voice_other m, other
|
||||
m.channel.voice(other)
|
||||
end
|
||||
|
||||
def exec_devoice m
|
||||
m.channel.devoice(m.user)
|
||||
end
|
||||
|
||||
def exec_devoice_other m, other
|
||||
m.channel.devoice(other)
|
||||
end
|
||||
|
||||
end
|
17
plugins/poilo.rb
Normal file
17
plugins/poilo.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# coding: utf-8
|
||||
|
||||
class Poilo < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/^[^!].+/, use_prefix: false, method: :exec_poilo)
|
||||
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
SYLLABE = %w(a i o u y oi eau au ou an eu)
|
||||
CONFIG = config
|
||||
|
||||
def exec_poilo m
|
||||
word = m.message.split.last
|
||||
syl = word.split(/[^aeiouy]/).last
|
||||
m.reply "poil au " + CONFIG["list"][syl] if not syl.nil?
|
||||
end
|
||||
end
|
76
plugins/points.rb
Normal file
76
plugins/points.rb
Normal file
|
@ -0,0 +1,76 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Points < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
include Botpop::Plugin::Database
|
||||
|
||||
match(/.*/, use_prefix: false, method: :save_last_user)
|
||||
match(/^!pstats?$/, use_prefix: false, method: :statistics)
|
||||
match(/^!pstats?u (\w+)$/, use_prefix: false, method: :statistics_for_user)
|
||||
match(/^!pstats?p (\w+)$/, use_prefix: false, method: :statistics_for_point)
|
||||
match(/^!p +(\w+)$/, use_prefix: false, method: :add_point_to_last)
|
||||
match(/^!p +(\w+) +(\w+)$/, use_prefix: false, method: :add_point_to_user)
|
||||
match(/hei(l|i)/i, use_prefix: false, method: :point_nazi)
|
||||
|
||||
HELP = ["!p <type> [to]", "!pstats", "!pstatsu <nick>", "!pstatsp <point>"]
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
@@users = {}
|
||||
@@lock = Mutex.new
|
||||
|
||||
def statistics m
|
||||
ret = Base::DB.fetch("SELECT points.type, COUNT(*) AS nb FROM points GROUP BY points.type ORDER BY nb DESC LIMIT 10;").all.map{|e| e[:type] + "(#{e[:nb]})"}.join(", ")
|
||||
# data = Base::DB.fetch("SELECT points.type, COUNT(points.*) AS nb FROM points GROUP BY points.type ORDER BY nb DESC LIMIT 10;").all
|
||||
# data.map!{|e| Base::DB.fetch("SELECT assigned_to FROM points GROUP BY type, assigned_to HAVING type = ? ORDER BY COUNT(*) DESC;", e[:type]).first.merge(e)}
|
||||
# ret = data.map{|e| e[:type] + "(#{e[:nb]}x #{e[:assigned_to]})"}.join(", ")
|
||||
m.reply "Top used: #{ret}"
|
||||
end
|
||||
|
||||
def statistics_for_user m, u
|
||||
ret = Base::DB.fetch("SELECT points.type, COUNT(*) AS nb FROM points WHERE assigned_to = ? GROUP BY points.type ORDER BY COUNT(*) DESC LIMIT 10;", u.downcase).all.map{|e| e[:type] + "(#{e[:nb]})"}.join(", ")
|
||||
m.reply "User #{u} has: #{ret}"
|
||||
end
|
||||
|
||||
def statistics_for_point m, p
|
||||
data = Base::DB.fetch("SELECT assigned_to, COUNT(*) AS nb FROM points GROUP BY type, assigned_to HAVING type = ? ORDER BY COUNT(*) DESC LIMIT 10;", p).all
|
||||
ret = data.map{|e| e[:assigned_to] + "(#{e[:nb]})"}.join(", ")
|
||||
m.reply "Point #{p}: #{ret}"
|
||||
end
|
||||
|
||||
def save_last_user m
|
||||
return if m.message.match(/^!p .+$/)
|
||||
@@lock.lock
|
||||
@@users[m.channel.to_s] = m.user.nick
|
||||
@@lock.unlock
|
||||
end
|
||||
|
||||
def add_point_to_last m, type
|
||||
return if @@users[m.channel.to_s].nil?
|
||||
nick = @@users[m.channel.to_s]
|
||||
count = add_point(m.user.nick, nick, type)
|
||||
m.reply "#{nick} has now #{count} points #{type} !"
|
||||
end
|
||||
|
||||
def add_point_to_user m, type, nick
|
||||
count = add_point(m.user.nick, nick, type)
|
||||
m.reply "#{nick} has now #{count} points #{type} !"
|
||||
end
|
||||
|
||||
def point_nazi m
|
||||
nick = m.user.nick
|
||||
count = add_point("self", nick, "nazi")
|
||||
m.reply "#{nick} has now #{count} points nazi !" if count % 10 == 0
|
||||
end
|
||||
|
||||
private
|
||||
def add_point(by, to, type)
|
||||
to.downcase!
|
||||
Base::DB[:points].insert({assigned_by: by,
|
||||
assigned_to: to,
|
||||
type: type,
|
||||
created_at: Time.now})
|
||||
Base::DB[:points].where(assigned_to: to, type: type).count
|
||||
end
|
||||
|
||||
end
|
12
plugins/points/PointModel.rb
Normal file
12
plugins/points/PointModel.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
require 'sequel'
|
||||
|
||||
class Point < Sequel::Model
|
||||
|
||||
def before_save
|
||||
return false if super == false
|
||||
self.created_at = Time.now
|
||||
end
|
||||
|
||||
set_dataset Base::DB[:points]
|
||||
|
||||
end
|
1
plugins/points/migrations
Symbolic link
1
plugins/points/migrations
Symbolic link
|
@ -0,0 +1 @@
|
|||
base/migrations/
|
|
@ -1,11 +1,11 @@
|
|||
#encoding: utf-8
|
||||
|
||||
trap('SIGINT') {
|
||||
BotpopPlugins::Proxy.database_users_write({})
|
||||
Botpop::Plugins::Proxy.database_users_write({})
|
||||
exit
|
||||
}
|
||||
|
||||
class Proxy < BotpopPlugin
|
||||
class Proxy < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match("!proxy list", use_prefix: false, method: :exec_proxy_list)
|
||||
|
@ -14,18 +14,17 @@ class Proxy < BotpopPlugin
|
|||
match("!proxy drop", use_prefix: false, method: :exec_proxy_drop)
|
||||
|
||||
HELP = ["!proxy list", "!proxy ip", "!proxy get", "!proxy drop"]
|
||||
CONFIG = Botpop::CONFIG['proxy'] || raise(MissingConfigurationZone, 'proxy')
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
|
||||
if ENABLED
|
||||
require 'htauth'
|
||||
require 'digest'
|
||||
end
|
||||
|
||||
LIMIT_USERS = CONFIG['limit_users'] || 1
|
||||
PASSWD_FILE = CONFIG['passwd_file'] || '/etc/squid3/passwords'
|
||||
IP = CONFIG['ip_addr'] || raise(MissingConfigurationEntry, 'ip_addr')
|
||||
PORT = CONFIG['ip_port'] || raise(MissingConfigurationEntry, 'ip_port')
|
||||
LIMIT_USERS = config['limit_users'] || 1
|
||||
PASSWD_FILE = config['passwd_file'] || '/etc/squid3/passwords'
|
||||
IP = config['ip_addr'] || raise(MissingConfigurationEntry, 'ip_addr')
|
||||
PORT = config['ip_port'] || raise(MissingConfigurationEntry, 'ip_port')
|
||||
|
||||
File.open(PASSWD_FILE, 'a') {}
|
||||
LOCKED_USERS = File.readlines(PASSWD_FILE)
|
||||
|
|
119
plugins/puppet.rb
Normal file
119
plugins/puppet.rb
Normal file
|
@ -0,0 +1,119 @@
|
|||
require 'date'
|
||||
|
||||
class Puppet < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/^!pm (\#*\w+) (.*)/, use_prefix: false, method: :send_privmsg)
|
||||
match(/^!join (\#\w+)/, use_prefix: false, method: :join)
|
||||
match(/^!part (\#\w+)/, use_prefix: false, method: :part)
|
||||
|
||||
# Email handlement
|
||||
EMAIL = '[[:alnum:]\.\-_]{1,64}@[[:alnum:]\.\-_]{1,64}'
|
||||
NICK = '\w+'
|
||||
# Registration of an email address - association with authname
|
||||
match(/^!mail register (#{EMAIL})$/, use_prefix: false, method: :register)
|
||||
match(/^!mail primary (#{EMAIL})$/, use_prefix: false, method: :make_primary)
|
||||
# Send email to user through its nickname (not safe)
|
||||
match(/^!(mail )?(let|send) (#{NICK}) (.+)/, use_prefix: false, method: :let)
|
||||
# Send email to user through one of its emails (safe)
|
||||
match(/^!(mail )?(let|send) (#{EMAIL}) (.+)/, use_prefix: false, method: :let)
|
||||
# Read email (based on nickname, authname, and emails)
|
||||
match(/^!(mail )?r(ead)?$/, use_prefix: false, method: :read)
|
||||
|
||||
HELP = ["!pm <#chat/nick> <message>", "!join <#chan>", "!part <#chan>", "!let <to> msg", "!read"] +
|
||||
["!mail read", "!mail send/let <...>", "!mail register address"]
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def send_privmsg m, what, message
|
||||
if what.match(/^\#.+/)
|
||||
send_privmsg_to_channel(what, message)
|
||||
else
|
||||
send_privmsg_to_user(what, message)
|
||||
end
|
||||
end
|
||||
|
||||
def join m, chan
|
||||
Channel(chan).join
|
||||
end
|
||||
|
||||
def part m, chan
|
||||
Channel(chan).part
|
||||
end
|
||||
|
||||
def register m, email
|
||||
begin
|
||||
Base::DB[:emails].insert(authname: m.user.authname,
|
||||
address: email,
|
||||
created_at: Time.now.utc,
|
||||
usage: 0)
|
||||
rescue => _
|
||||
return m.reply "Error, cannot register this email !"
|
||||
end
|
||||
return m.reply "Email #{email} registered for you, #{m.user.authname}"
|
||||
end
|
||||
|
||||
def make_primary m, email
|
||||
a = get_addresses(m.user).where(address: email)
|
||||
return m.reply "No your email #{email}" if a.first.nil?
|
||||
get_addresses(m.user).update(primary: false)
|
||||
a.update(primary: true)
|
||||
m.reply "Your primary email #{m.user.nick} is now #{email}"
|
||||
end
|
||||
|
||||
def let m, _, _, to, msg
|
||||
log "New message addressed to #{to} to send"
|
||||
# insert new message in database
|
||||
from = Base::DB[:emails].where(authname: m.user.authname, primary: true).select(:address).first
|
||||
from = from && from[:address] || m.user.nick
|
||||
Base::DB[:messages].insert(author: from,
|
||||
dest: to,
|
||||
content: msg.strip,
|
||||
created_at: Time.now,
|
||||
read_at: nil)
|
||||
Base::DB[:emails].where(address: to).update('usage = usage+1')
|
||||
end
|
||||
|
||||
def read m, _
|
||||
msg = get_messages(m.user).first
|
||||
if msg.nil?
|
||||
send_privmsg_to_user m.user, "No message."
|
||||
return
|
||||
end
|
||||
Base::DB[:messages].where(id: msg[:id]).update(read_at: Time.now)
|
||||
date = msg[:created_at]
|
||||
if Date.parse(Time.now.to_s) == Date.parse(date.to_s)
|
||||
date = date.strftime("%H:%M:%S")
|
||||
else
|
||||
date = date.strftime("%B, %d at %H:%M:%S")
|
||||
end
|
||||
send_privmsg_to_user m.user, "##{msg[:id]}# #{date} -- from #{msg[:author]}"
|
||||
send_privmsg_to_user m.user, msg[:content]
|
||||
end
|
||||
|
||||
listen_to :join, method: :bip_on_join
|
||||
|
||||
def bip_on_join m
|
||||
nb = Base::DB[:messages].where(dest: m.user.nick, read_at: nil).count
|
||||
send_privmsg_to_user m.user, "#{m.user.nick}: You have #{nb} message unread." unless nb.zero?
|
||||
end
|
||||
|
||||
private
|
||||
def send_privmsg_to_channel chan, msg
|
||||
Channel(chan).send(msg.strip)
|
||||
end
|
||||
|
||||
def send_privmsg_to_user user, msg
|
||||
User(user).send(msg.strip)
|
||||
end
|
||||
|
||||
def get_messages user
|
||||
emails = Base::DB[:emails].where(authname: user.authname).select(:address).all.map(&:values).flatten
|
||||
Base::DB[:messages].where(dest: [user.nick, user.authname] + emails).where(read_at: nil)
|
||||
end
|
||||
|
||||
def get_addresses user
|
||||
Base::DB[:emails].where(authname: user.authname)
|
||||
end
|
||||
|
||||
end
|
42
plugins/root_me.rb
Normal file
42
plugins/root_me.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
require "base64"
|
||||
|
||||
class RootMe < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
FloatRegexp = "\d+(\.\d+)?"
|
||||
match(/!ep1 (\w+)/, use_prefix: false, method: :start_ep1)
|
||||
match(/!ep2 (\w+)/, use_prefix: false, method: :start_ep2)
|
||||
match(/^(#{FloatRegexp}) ?\/ ?(#{FloatRegexp})$/, use_prefix: false, method: :play_ep1)
|
||||
match(/^(\d+) ?\/ ?(\d+)$/, use_prefix: false, method: :play_ep1)
|
||||
match(/^(\w+)$/, use_prefix: false, method: :play_ep2)
|
||||
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
def start_ep1 m, botname
|
||||
bot = User(botname)
|
||||
bot.send "!ep1"
|
||||
end
|
||||
|
||||
def start_ep2 m, botname
|
||||
bot = User(botname)
|
||||
bot.send "!ep2"
|
||||
end
|
||||
|
||||
def play_ep1 m, n1, n2
|
||||
r = n1.to_f**(0.5) * n2.to_f
|
||||
str = "!ep1 -rep #{r.round 2}"
|
||||
puts str
|
||||
m.reply str
|
||||
# response will be logged by the bot, check the log
|
||||
end
|
||||
|
||||
def play_ep2 m, b64
|
||||
r = Base64.decode64(b64)
|
||||
str = "!ep2 -rep #{r}"
|
||||
puts str
|
||||
m.reply str
|
||||
# response will be logged by the bot, check the log
|
||||
end
|
||||
|
||||
end
|
|
@ -1,17 +1,16 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class SayGoodBye < BotpopPlugin
|
||||
class SayGoodBye < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/^!sg [\w\-\.].+/, use_prefix: false, method: :exec_sg)
|
||||
|
||||
HELP = ["!sg src_name"]
|
||||
CONFIG = Botpop::CONFIG['say_goodbye'] || raise(MissingConfigurationZone, self.name)
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
|
||||
def exec_sg m
|
||||
arg = m.message.split.last
|
||||
m.reply CONFIG[arg].shuffle.first
|
||||
m.reply config[arg].sample
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Searchable < BotpopPlugin
|
||||
class Searchable < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
CONFIG = Botpop::CONFIG['searchable'] || raise(MissingConfigurationZone, self.to_s)
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
|
||||
ENABLED = config['enable'].nil? ? true : config['enable']
|
||||
|
||||
VALUES = CONFIG.values.map{|e|"!"+e}.join(', ')
|
||||
KEYS = CONFIG.keys.map{|e|"!"+e}.join(', ')
|
||||
HELP = CONFIG.keys.map{|e|"!"+e+" [search]"}
|
||||
match(/\!(#{CONFIG.keys.join('|')}) .+/, use_prefix: false, method: :exec_search)
|
||||
VALUES = config.values.map{|e|"!"+e}.join(', ')
|
||||
KEYS = config.keys.map{|e|"!"+e}.join(', ')
|
||||
HELP = config.keys.map{|e|"!"+e+" [search]"}
|
||||
CONFIG = config
|
||||
match(/\!(#{config.keys.join('|')}) .+/, use_prefix: false, method: :exec_search)
|
||||
|
||||
def exec_search m
|
||||
msg = BotpopBuiltins.get_msg m
|
||||
msg = Botpop::Builtins.get_msg m
|
||||
url = CONFIG[m.params[1..-1].join(' ').gsub(/\!([^ ]+) .+/, '\1')]
|
||||
url = url.gsub('___MSG___', msg)
|
||||
m.reply url
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
#encoding: utf-8
|
||||
|
||||
class Taggle < BotpopPlugin
|
||||
class Taggle < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!tg (.+)/, use_prefix: false, method: :exec_tg)
|
||||
|
||||
HELP = ["!tg [nick]"]
|
||||
CONFIG = Botpop::CONFIG['taggle'] || {} || raise(MissingConfigurationZone, self.to_s)
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable'] rescue true
|
||||
CONFIG = config(safe: true) || {}
|
||||
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
|
||||
NTIMES = CONFIG['ntimes'] || 10
|
||||
WAIT = CONFIG['wait'] || 0.3
|
||||
|
||||
def exec_tg c, m, who
|
||||
def cmd_allowed? m
|
||||
return Base.cmd_allowed? m, ["tg"]
|
||||
end
|
||||
|
||||
def exec_tg m, who
|
||||
return if not cmd_allowed? m
|
||||
@@tg_lock ||= Mutex.new
|
||||
@@tg_lock.lock
|
||||
begin
|
||||
NTIMES.times do
|
||||
c.User(who).send("tg #{who}")
|
||||
User(who).send("tg #{who}")
|
||||
sleep WAIT
|
||||
end
|
||||
ensure
|
||||
|
|
36
plugins/youtube.rb
Normal file
36
plugins/youtube.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
require 'mechanize'
|
||||
|
||||
class Youtube < Botpop::Plugin
|
||||
include Cinch::Plugin
|
||||
|
||||
match(/!yt (.+)/, use_prefix: false, method: :find_youtube_video)
|
||||
|
||||
HELP = ["!yt title"]
|
||||
ENABLED = config['enable'].nil? ? false : config['enable']
|
||||
CONFIG = config
|
||||
|
||||
private
|
||||
def search_url(title)
|
||||
CONFIG['search_url'].gsub('___MSG___', title)
|
||||
end
|
||||
def reduce_url(url)
|
||||
CONFIG['reduce_url'].gsub('___ID___', url.gsub(/^(.+)(v=)(\w+)$/, '\3'))
|
||||
end
|
||||
def display(result)
|
||||
CONFIG['display']
|
||||
.gsub('___TITLE___', result[:title])
|
||||
.gsub('___URL___', reduce_url(result[:url]))
|
||||
end
|
||||
public
|
||||
|
||||
def find_youtube_video m, title
|
||||
e = Mechanize.new
|
||||
e.get(search_url(title))
|
||||
result = {
|
||||
title: e.page.at(".item-section li").at('h3').text,
|
||||
url: e.page.at(".item-section li").at('a')[:href],
|
||||
}
|
||||
m.reply display(result)
|
||||
end
|
||||
|
||||
end
|
|
@ -17,8 +17,8 @@ class TestBotbot < Test::Unit::TestCase
|
|||
assert(Botpop::CONFIG)
|
||||
assert(Botpop::TARGET)
|
||||
assert(Botpop::PluginInclusion.class == Module)
|
||||
assert(BotpopBuiltins.class == Module)
|
||||
assert(BotpopPlugins.class == Module)
|
||||
assert(Botpop::Builtins.class == Module)
|
||||
assert(Botpop::Plugin.class == Class)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user