Compare commits

..

No commits in common. "master" and "v0" have entirely different histories.
master ... v0

64 changed files with 891 additions and 2083 deletions

10
.gitignore vendored
View File

@ -1,10 +1,4 @@
*.swp *.swp
*~ *~
*.log .log
*.sqlite3 plugins/logger_user.yml
old/
modules_config.yml
plugins/log_user.yml

View File

@ -1,78 +0,0 @@
# 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
View File

@ -1,29 +1,7 @@
source 'https://rubygems.org' source 'https://rubygems.org'
#irc gem 'net-ping'
gem 'cinch' gem 'cinch'
# scrap yt
gem 'mechanize'
# debug
gem 'pry' gem 'pry'
gem 'colorize' gem 'colorize'
#network
gem 'net-ping'
#proxy
gem 'htauth' gem 'htauth'
#iamalive
gem 'sequel'
# gem 'sqlite3'
gem 'pg'
#encrypt
gem 'tor257'
#other
gem 'nomorebeer'
gem 'i18n'

View File

@ -1,47 +1,19 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
cinch (2.2.7) cinch (2.2.6)
coderay (1.1.0) coderay (1.1.0)
colorize (0.7.7) colorize (0.7.7)
domain_name (0.5.25) highline (1.7.2)
unf (>= 0.0.5, < 1.0.0) htauth (1.2.0)
htauth (2.0.0) highline (~> 1.6)
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) method_source (0.8.2)
mime-types (2.6.2) net-ping (1.7.7)
mini_portile (0.6.2) pry (0.10.1)
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) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
slop (~> 3.4) slop (~> 3.4)
sequel (4.27.0)
slop (3.6.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 PLATFORMS
ruby ruby
@ -50,14 +22,8 @@ DEPENDENCIES
cinch cinch
colorize colorize
htauth htauth
i18n
mechanize
net-ping net-ping
nomorebeer
pg
pry pry
sequel
tor257
BUNDLED WITH BUNDLED WITH
1.11.2 1.10.5

114
README.md
View File

@ -1,30 +1,21 @@
# botpop # botpop
[![Code Climate](https://codeclimate.com/github/Nephos/botpop/badges/gpa.svg)](https://codeclimate.com/github/Nephos/botpop) [![Code Climate](https://codeclimate.com/github/pouleta/botpop/badges/gpa.svg)](https://codeclimate.com/github/pouleta/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 : Ruby 2 or greater is required. To be compatible with Ruby 1.9, you can try :
```bash ```bash
sed 's/prepend/include/g' -i botpop.rb sed 's/prepend/include/g' -i botpop.rb
``` ```
but i did never try... You better update ruby ! ;) but i did never try... You better update ruby ! ;)
Too install the stuff, just do :
```bash ```bash
bundle install # install the required gems bundle install
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 ## Arguments
By default, only the first occurence of the argument will be used, unless specified. By default, only the first occurence of the argument will be used, unless specified.
@ -60,19 +51,12 @@ end
Some official plugins are developped. You can propose your own creation by pull request, or add snippets link to the wiki. Some official plugins are developped. You can propose your own creation by pull request, or add snippets link to the wiki.
## List ## List
- [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. - [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/Nephos/botpop/blob/master/plugins/network.rb) : an usefull plugin with commands __ping, ping ip, ping http, traceroute, dos attack and poke__ - [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/Nephos/botpop/blob/master/plugins/searchable.rb) : a little plugin providing irc research with engines like __google, wikipedia, ruby-doc, etc...__ - [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...__
- [Proxy](https://github.com/Nephos/botpop/blob/master/plugins/proxy.rb) : an audacious plugin to create user access to a local proxy - [Coupon](https://github.com/pouleta/botpop/blob/master/plugins/coupons.rb) : the original aim of the bot. It get coupons for the challenge __pathwar__
- [Log](https://github.com/Nephos/botpop/blob/master/plugins/log.rb) : simple logger - [Intranet](https://github.com/pouleta/botpop/blob/master/plugins/intranet.rb) : an useless plugin to check the intranet of epitech
- [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. - [Proxy](https://github.com/pouleta/botpop/blob/master/plugins/proxy.rb) : an audacious plugin to create user access to a local proxy
- [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 ## Create your own
@ -83,94 +67,64 @@ You should take the time to read the documentation before developping anything.
### Example of new plugin ### Example of new plugin
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) 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)
First, put your ruby code file in ``plugins/``, and put your code in the scope : First, put your ruby code file in ``plugins/``, and put your code in the scope :
```ruby ```ruby
class MyFuryPlugin < Botpop::Plugin module BotpopPlugins
include Cinch::Plugin module MyFuryPlugin
def self.exec_whatkingofanimal m
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
...code... ...code...
end end
end
``` ```
### Matching messages ### Matching messages
To create a matching to respond to a message, you have to specifie in your plugin : To create a matching to respond to a message, you have to specifie in your plugin :
```ruby ```ruby
class MyFuryPlugin < Botpop::Plugin module BotpopPlugins
include Cinch::Plugin module MyFuryPlugin
match(/!whatkingofanimal.*/, use_prefix: false, method: :exec_whatkingofanimal) MATCH = lambda do |parent, plugin|
parent.on :message, /!whatkingofanimal.*/ do |m| plugin.exec_whatkingofanimal m end
end
...code... ...code...
end end
end
``` ```
### Add entry to the !help command ### Add entry to the !help command
The __official plugin__ [Base](https://github.com/Nephos/botpop/blob/master/plugins/base.rb) provides the command __!help__ and __!help plugin__. The __official plugin__ [Base](https://github.com/pouleta/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. 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.__ __The strings should be as short as possible.__
You should write it like the following: You should write it like the following:
```ruby ```ruby
class MyFuryPlugin < Botpop::Plugin module BotpopPlugins
module MyFuryPlugin
HELP = ["!whatkingofanimal", "!animallist", "!checkanimal [type]"] HELP = ["!whatkingofanimal", "!animallist", "!checkanimal [type]"]
...code... ...code...
end end
end
``` ```
### Enable and disable plugin ### Enable and disable plugin
You can enable or disable plugin by using the constant __ENABLED__. 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. The constant must be defined by the developper of the plugin.
For example, you can implement it like : For example, you can implement it like :
```ruby ```ruby
class MyFuryPlugin < Botpop::Plugin module BotpopPlugins
ENABLED = config['enable'].nil? ? true : config['enable'] module MyFuryPlugin
CONFIG = Botpop::CONFIG['myfurry'] || raise(MissingConfigurationZone, 'myfurry')
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
end
end end
``` ```
Then, a simple line in the ``modules_configuration.yml`` file should be enough. 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)

View File

@ -1,15 +0,0 @@
# 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
```

View File

@ -1,17 +1,7 @@
#encoding: utf-8 #encoding: utf-8
require 'yaml' task :default => [:test]
CONFIG = YAML.load_file("modules_config.yml")
#require 'sequel' task :test do
#DB_BASE = Sequel.connect(CONFIG['base']['database']) ruby "test/test.rb"
#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
end

View File

@ -1,8 +1,8 @@
# encoding: utf-8 # encoding: utf-8
class Arguments class Arguments
# @param name [String] the option to search # @arg : name [String] the option to search
# @param name [Array] the options to search (multiples keys avaliables) # @arg : name [Array] the options to search (multiples keys avaliables)
def get_one_argument(name, default_value) def get_one_argument(name, default_value)
if name.is_a? String if name.is_a? String
i = @argv.index(name) i = @argv.index(name)
@ -83,14 +83,18 @@ class Arguments
end end
def disable_plugins def disable_plugins
i = 0
plugins = [] plugins = []
argv = @argv.dup argv = @argv.dup
loop do while i
i = argv.index('--plugin-disable') i = argv.index('--plugin-disable')
break unless i if i
plugins << @argv[i + 1] plugin = argv[i + 1]
plugin = plugin + '.rb' if plugin[-4..-1] != '.rb'
plugins << File.expand_path(plugin, plugin_directory)
argv = argv[(i+2)..(-1)] argv = argv[(i+2)..(-1)]
end end
end
return plugins return plugins
end end

View File

@ -13,54 +13,42 @@ require 'yaml'
require 'colorize' require 'colorize'
require_relative 'arguments' require_relative 'arguments'
require_relative 'botpop_plugin_inclusion' require_relative 'builtin'
require_relative 'builtins'
require_relative 'database' require_relative "botpop_plugin_inclusion"
$botpod_arguments ||= ARGV
class Botpop 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')
rescue Errno::ENOENT
puts "No version specified".red
return "???"
end
end
def self.include_plugins
PluginInclusion.plugins_include! ARGUMENTS
end
def self.load_plugins
Module.constants.select{ |m|
(m = Module.const_get(m) rescue false) and
(m.is_a?(Class)) and
(m.ancestors.include?(Plugin)) and
(m.included_modules.include?(Cinch::Plugin))
}.
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 # FIRST LOAD THE CONFIGURATION
ARGUMENTS = Arguments.new(ARGV) ARGUMENTS = Arguments.new($botpod_arguments)
VERSION = load_version() VERSION = IO.read('version')
CONFIG = YAML.load_file(ARGUMENTS.config_file) CONFIG = YAML.load_file(ARGUMENTS.config_file)
TARGET = /[[:alnum:]_\-\.]+/ TARGET = /[[:alnum:]_\-\.]+/
include_plugins()
PLUGINS = load_plugins() PluginInclusion::plugins_include! ARGUMENTS
# THEN LOAD / NOT THE PLUGINS
def self.plugins_load!
@@plugins = []
BotpopPlugins.constants.each do |const|
plugin = BotpopPlugins.const_get(const)
next if not plugin.is_a? Module
if plugin::ENABLED == false
puts "Disabled plugin #{plugin}".yellow if $botpop_include_verbose != false
next
end rescue nil
puts "Load plugin #{plugin}".green if $botpop_include_verbose != false
# prepend plugin
@@plugins << plugin
end
end
plugins_load!
def self.plugins
@@plugins.dup
end
def start def start
@engine.start @engine.start
@ -75,7 +63,10 @@ class Botpop
c.port = ARGUMENTS.port c.port = ARGUMENTS.port
c.user = ARGUMENTS.user c.user = ARGUMENTS.user
c.nick = ARGUMENTS.nick c.nick = ARGUMENTS.nick
c.plugins.plugins = PLUGINS end
@@plugins.each do |plugin|
puts "Load matchings of the plugin #{plugin}".green
plugin::MATCH.call(self, plugin) rescue puts "No matching found for #{plugin}".red
end end
end end
end end
@ -84,10 +75,5 @@ end
if __FILE__ == $0 if __FILE__ == $0
$bot = Botpop.new $bot = Botpop.new
trap("SIGINT") do
puts "\b"
puts "SIGINT Catched"
exit
end
$bot.start $bot.start
end end

View File

@ -1,7 +1,10 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
#encoding: utf-8 #encoding: utf-8
class Botpop
module PluginInclusion module PluginInclusion
def self.plugin_error_failure! e, f def self.plugin_error_failure! e, f
STDERR.puts "Error during loading the file #{f}".red STDERR.puts "Error during loading the file #{f}".red
STDERR.puts "#{e.class}: #{e.message}".red.bold STDERR.puts "#{e.class}: #{e.message}".red.bold
@ -11,7 +14,6 @@ module PluginInclusion
end end
def self.plugin_include! f def self.plugin_include! f
return false if File.basename(f) == "example.rb"
begin begin
if $botpop_include_verbose != false if $botpop_include_verbose != false
puts "Loading plugin file ... " + f.green + " ... " + require_relative(f).to_s puts "Loading plugin file ... " + f.green + " ... " + require_relative(f).to_s
@ -33,3 +35,5 @@ module PluginInclusion
end end
end end
end

45
builtin.rb Normal file
View File

@ -0,0 +1,45 @@
# encoding: utf-8
module BotpopBuiltins
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
end
class MissingConfigurationEntry < RuntimeError
end

View File

@ -1,43 +0,0 @@
# encoding: utf-8
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
end
end
class MissingConfigurationZone < RuntimeError
end
class MissingConfigurationEntry < RuntimeError
end

View File

@ -3,5 +3,3 @@ moul -- guest
ernestjkaufman -- guest ernestjkaufman -- guest
FolenScare -- guest FolenScare -- guest
JoycePF -- guest JoycePF -- guest
Deborah -- guest
Shigugu -- guest

View File

@ -1,21 +0,0 @@
#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

View File

@ -1,67 +1,5 @@
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: searchable:
#yt: https://www.youtube.com/results?search_query=___MSG___ # use the plugin youtube instead yt: https://www.youtube.com/results?search_query=___MSG___
xv: http://www.xvideos.com/?k=___MSG___ xv: http://www.xvideos.com/?k=___MSG___
yp: https://www.youporn.com/search/?query=___MSG___ yp: https://www.youporn.com/search/?query=___MSG___
@ -99,6 +37,17 @@ searchable:
pkmn: http://pokemondb.net/search?q=___MSG___ pkmn: http://pokemondb.net/search?q=___MSG___
tek: https://intra.epitech.eu/user/___MSG___ tek: https://intra.epitech.eu/user/___MSG___
base:
enable: true
help_wait_duration: 120
logger:
enable: true
file: logger.log
intranet:
enable: true
network: network:
enable: true enable: true
dos_wait: 10s dos_wait: 10s
@ -106,6 +55,10 @@ network:
trace_duration_init: 0.3s trace_duration_init: 0.3s
trace_duration_incr: 0.1s trace_duration_incr: 0.1s
coupons:
enable: false
display_coupons: true
proxy: proxy:
enable: false enable: false
ip_addr: localhost ip_addr: localhost
@ -117,20 +70,10 @@ epitech:
enable: false enable: false
iamalive: iamalive:
enable: true enable: false
default_mode: live database_file: plugins/iamalive.database.yaml
database:
adapter: postgres
host: localhost
port: 5432
user: root
password: toor
database: botpop_iamalive
cequetudisnaaucunsens: say_goodbye:
enabled: true
saygoodbye:
chapui_s: chapui_s:
- "@chapui_s t'es là ?" - "@chapui_s t'es là ?"
- "chapui_s !!! T'es où ?" - "chapui_s !!! T'es où ?"
@ -159,3 +102,6 @@ saygoodbye:
- "No râge de mon gradÂge !" - "No râge de mon gradÂge !"
poulet_a: poulet_a:
- "Nan mais c'est un scandale ! Pourquoi j'ai grade A ?" - "Nan mais c'est un scandale ! Pourquoi j'ai grade A ?"
example:
enable: false

2
plugins/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
coupon_login.yml
iamalive.database.yaml

View File

@ -1,46 +0,0 @@
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

View File

@ -1,85 +1,76 @@
#encoding: utf-8 #encoding: utf-8
require "i18n" module BotpopPlugins
I18n.config.available_locales = [:en, :fr] module Base
class Base < Botpop::Plugin MATCH = lambda do |parent, plugin|
include Cinch::Plugin parent.on :message, /!troll .+/ do |m| plugin.exec_troll m end
include Botpop::Plugin::Database parent.on :message, "!version" do |m| plugin.exec_version m end
parent.on :message, "!code" do |m| plugin.exec_code m end
match(/^!troll .+/ , use_prefix: false, method: :exec_troll) parent.on :message, "!cmds" do |m| plugin.exec_help m end
match "!version" , use_prefix: false, method: :exec_version parent.on :message, "!help" do |m| plugin.exec_help m end
match "!code" , use_prefix: false, method: :exec_code parent.on :message, /!help \w+/ do |m| plugin.exec_help_plugin m end
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("!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 end
HELP = ["!troll [msg]", "!version", "!code", "!help [plugin]", "!cmds"]
CONFIG = Botpop::CONFIG['base'] || raise(MissingConfigurationZone, 'base')
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
def help_wait_before_quit def self.help_wait_before_quit
HELP_WAIT_DURATION.times do HELP_WAIT_DURATION.times do
sleep 1 sleep 1
@@help_time += 1 @help_time += 1
end end
end end
def help_get_plugins_str def self.help_get_plugins_str
["Plugins found : " + Botpop::PLUGINS.size.to_s] + ["Plugins found : " + Botpop.plugins.size.to_s] +
Botpop::PLUGINS.map do |plugin| Botpop.plugins.map do |plugin|
plugin.to_s.split(':').last plugin.to_s.split(':').last + ': ' + plugin::HELP.join(', ') rescue nil
end.compact end.compact
end end
HELP_WAIT_DURATION = config['help_wait_duration'] || 120 HELP_WAIT_DURATION = CONFIG['help_wait_duration'] || 120
def help m def self.help m
m.reply help_get_plugins_str.join(', ') @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
end end
def exec_version m def self.exec_version m
m.reply Botpop::VERSION m.reply Botpop::VERSION
end end
def exec_code m def self.exec_code m
m.reply "https://github.com/Nephos/botpop" m.reply "https://github.com/pouleta/botpop"
end end
def exec_help m def self.exec_help m
help m help m
end end
def exec_help_plugin m def self.exec_help_plugin m
module_name = m.message.split(" ").last.downcase module_name = m.message.split(" ").last
i = Botpop::PLUGINS.map{|e| e.to_s.split(":").last.downcase}.index(module_name) i = Botpop.plugins.map{|e| e.to_s.split(":").last}.index(module_name)
if i.nil? if i.nil?
m.reply "No plugin #{module_name}" m.reply "No plugin #{module_name}"
return return
end end
plugin = Botpop::PLUGINS[i] plugin = Botpop.plugins[i]
m.reply plugin::HELP.join(', ') m.reply plugin::HELP.join(', ')
end end
def exec_troll m def self.exec_troll m
# hours = (Time.now.to_i - Time.gm(2015, 04, 27, 9).to_i) / 60 / 60 # hours = (Time.now.to_i - Time.gm(2015, 04, 27, 9).to_i) / 60 / 60
s = Botpop::Builtins.get_msg m s = BotpopBuiltins.get_msg m
url = "http://www.fuck-you-internet.com/delivery.php?text=#{s}" url = "http://www.fuck-you-internet.com/delivery.php?text=#{s}"
m.reply url m.reply url
end end
end end
end

View File

@ -1,21 +0,0 @@
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

View File

@ -1,10 +0,0 @@
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

View File

@ -1,11 +0,0 @@
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

View File

@ -1,12 +0,0 @@
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

View File

@ -1,14 +0,0 @@
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

View File

@ -1,14 +0,0 @@
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

View File

@ -1,10 +0,0 @@
Sequel.migration do
change do
create_table(:eips) do
primary_key :id
String :author
String :title
DateTime :created_at
end
end
end

View File

@ -1,70 +0,0 @@
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

View File

@ -1,68 +0,0 @@
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

View File

@ -0,0 +1,7 @@
# This file is independent of /modules_config.yml because it should stay private
creditentials:
username: login
password: pass
organisation: orga
api_url: https://api.pathwar.net/
api_coupon_url: https://api.pathwar.net/organization-coupons/

78
plugins/coupons.rb Normal file
View File

@ -0,0 +1,78 @@
#encoding: utf-8
require 'uri'
require 'net/http'
require 'json'
module BotpopPlugins
module Coupons
COUPON_REGEXP = "[A-Fa-f0-9]{32}"
MATCH = lambda do |parent, plugin|
# parent.on :message, /coupon(.+)?: .+/ do |m| plugin.exec_coupon m end
parent.on :message, /.*#{COUPON_REGEXP}.*/ do |m| plugin.exec_coupon_somewhere m end
end
# HELP = ["coupon: [...]"]
HELP = []
CONFIG = Botpop::CONFIG['coupons'] || raise(MissingConfigurationZone, 'coupons')
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
if ENABLED
SECRET_CONFIG = YAML.load_file('plugins/coupon_login.yml')['creditentials']
USER = SECRET_CONFIG['username']
PASS = SECRET_CONFIG['password']
ORGA = SECRET_CONFIG['organisation']
APIU = SECRET_CONFIG['api_coupon_url']
URL = URI(APIU)
end
def self.get_coupon m
coupon = m.params[1..-1].join(' ').gsub(/(coupon(.+)?:)/, '').split.first
coupon = coupon.gsub(/[^A-z0-9\.\-_]/, '') # secure a little
coupon
end
def self.get_coupons_somewhere m
coupons = m.params[1..-1].join(' ').scan(/#{COUPON_REGEXP}/)
end
def self.send_coupon coupon
@http ||= Net::HTTP.new(URL.host, URL.port)
@http.use_ssl = true
# http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(URL)
request.add_field('Content-Type', 'application/json')
request.basic_auth USER, PASS
request.body = {'coupon' => coupon, 'organization' => ORGA}.to_json
response = @http.request(request)
@response = response
@request = request
response
end
def self.validate_coupon m, coupon
begin
response = send_coupon coupon
valid_response = response.code[0] == '2'
str = "#{coupon} " + (valid_response ? 'validated' : "failed (#{response.code})")
m.reply coupon if CONFIG['display_coupons']
rescue => e
m.reply "#{coupon} buggy"
end
end
# `curl https://api.pathwar.net/organization-coupons/#{coupon} -u '#{USER}:#{PASS}' -X GET`
def self.exec_coupon m
coupon = get_coupon m
validate_coupon m, coupon
end
def self.exec_coupon_somewhere m
coupons = get_coupons_somewhere m
coupons.each do |coupon|
validate_coupon m, coupon
end
end
end
end

View File

@ -1,12 +0,0 @@
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

View File

@ -1,98 +0,0 @@
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

View File

@ -1,12 +0,0 @@
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

View File

@ -1,84 +0,0 @@
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

View File

@ -1,22 +0,0 @@
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

View File

@ -1,35 +0,0 @@
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

View File

@ -1,20 +0,0 @@
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

30
plugins/epitech.rb Normal file
View File

@ -0,0 +1,30 @@
#encoding: utf-8
module BotpopPlugins
module Epitech
NAME = self.to_s.split(':').last
MATCH = lambda do |parent, plugin|
parent.on :message, /!admin/ do |m| plugin.exec_admin m end
parent.on :message, /!bocal/ do |m| plugin.exec_bocal m end
parent.on :message, /!astek/ do |m| plugin.exec_astek m end
end
HELP = ["!admin", "!bocal", "!astek"]
CONFIG = Botpop::CONFIG['epitech'] || raise(MissingConfigurationZone, NAME)
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
ASTEKS = CONFIG['asteks'] || []
def self.exec_admin m
m.reply "Afk"
end
def self.exec_bocal m
m.reply "Bataaaaaaaards !!"
end
def self.exec_astek m
m.reply "Enculéééééééés !!"
end
end
end

View File

@ -1,14 +1,19 @@
class MyFury < Botpop::Plugin #encoding: utf-8
include Cinch::Plugin
match(/!whatkingofanimal.*/, use_prefix: false, method: :exec_whatkingofanimal) module BotpopPlugins
module MyFuryPlugin
NAME = self.to_s.split(':').last
MATCH = lambda do |parent, plugin|
parent.on :message, /!whatkingofanimal.*/ do |m| plugin.exec_whatkingofanimal m end
end
HELP = ["!whatkingofanimal", "!animallist", "!checkanimal [type]"] HELP = ["!whatkingofanimal", "!animallist", "!checkanimal [type]"]
ENABLED = config['enable'].nil? ? false : config['enable'] CONFIG = Botpop::CONFIG['example'] || raise(MissingConfigurationZone, NAME)
CONFIG = config ENABLED = CONFIG['enable'].nil? ? false : CONFIG['enable']
def exec_whatkingofanimal m def self.exec_whatkingofanimal m
m.reply "Die you son of a" + ["lion", "pig", "red panda"].sample + " !!" m.reply "Die you son of a" + ["lion", "pig", "red panda"].shuffle.first + " !!"
end end
end end
end

View File

@ -1,111 +1,67 @@
class IAmAlive < Botpop::Plugin # encoding: utf-8
include Cinch::Plugin
include Botpop::Plugin::Database
match(/^[^!].*/, use_prefix: false, method: :register_entry) # 4.times {puts "=".yellow * 74}
match(/^[^!].*/, use_prefix: false, method: :react_on_entry) # puts "THE IAMALIVE PLUGINS IN INSANE. DISABLE BY USING --plugin-disable iamalive".red
match(/^!iaa reac(tivity)?$/, use_prefix: false, method: :get_reactivity) # 4.times {puts "=".yellow * 74}
match(/^!iaa reac(tivity)? \d{1,3}$/, use_prefix: false, method: :set_reactivity) # sleep 1
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) module BotpopPlugins
ENABLED = CONFIG['enable'] || false module IAmAlive
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 MATCH = lambda do |parent, plugin|
@@reactivity = config['reactivity'] || 50 parent.on :message, /.+/ do |m| plugin.exec_learn m end
parent.on :message, /.+/ do |m| plugin.exec_speak m end
def cmd_allowed? m parent.on :message, "!iaa clean" do |m| plugin.exec_clean m end
return Base.cmd_allowed? m, ["iaa"]
end end
HELP = ["I'm learning from you"]
CONFIG = Botpop::CONFIG['iamalive'] || raise(MissingConfigurationZone, 'iamalive')
ENABLED = CONFIG['enable'].nil? ? false : CONFIG['enable']
if ENABLED if ENABLED
DB_CONFIG = self.db_config = CONFIG['database'] DATABASE_FILE = CONFIG['database_file'] || raise(MissingConfigurationEntry, 'iamalive::database_file')
DB = self.db_connect! File.open(DATABASE_FILE, 'r') rescue init_database
require_relative 'iamalive/entry' # DISABLED MAY BE CONFIGURED, DEFAULT IS TRUE
end end
def register_entry m def self.init_database
Entry.create(user: (m.user.authname || m.user.nick), message: m.message, channel: m.channel.to_s) f = File.open(DATABASE_FILE, 'w')
forget_older! if rand(1..100) == 100 f.write("learn:\n")
f.close
end end
def react_on_entry m def self.open_database
return if @@mode != :live begin
e = Entry.where('LOWER(message) = LOWER(?)', m.message).select(:id).all.map(&:id).map{|x| x+1} @iamalive_db = YAML.load_file(DATABASE_FILE)['learn'].to_a
if @@reactivity > rand(1..100) rescue
answer_to(m, e) init_database
retry
end end
end end
private # Store in a database
def answer_to m, e def self.exec_learn m
a = Entry.where(id: e).to_a.sample line = m.params[1..-1].join(' ').to_yaml.gsub("--", " ").gsub("...\n", "")
if not a.nil? return if line.include?("VERSION") or line.match /\A["']?!/ or line.match(/\Ahttp/)
sleep(a.message.split.size.to_f / 10) f = File.open(DATABASE_FILE, 'a')
m.reply a.message f.write(line)
Entry.create(user: "self", message: a.message, channel: m.channel.to_s) f.close
end # m.reply "Learn: #{line}"
end end
def forget_older! PROBA = 6
log "Forget the older entry" def self.exec_speak m
Entry.first.delete if Random.rand(PROBA).zero?
end open_database
public i = Random.rand(@iamalive_db.size)
m.reply @iamalive_db[i]
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 else
nb = nb.to_i if not nb.nil? # m.reply "no reply"
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
end end
def get_last m, user def self.exec_clean m
user.strip! if user init_database
last = Entry.where(channel: m.channel.to_s, user: (user || "self")).last
m.reply "#{user}: #{last ? last.message : 'no message found'}"
end end
end end
end

View File

@ -1,29 +0,0 @@
# 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.

View File

@ -1,9 +0,0 @@
require 'sequel'
class IAmAlive
class Admin < Sequel::Model
set_dataset DB[:admins]
end
end

View File

@ -1,31 +0,0 @@
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

View File

@ -1,12 +0,0 @@
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

View File

@ -1,8 +0,0 @@
Sequel.migration do
change do
create_table(:admins) do
primary_key :id
String :user, null: false
end
end
end

41
plugins/intranet.rb Normal file
View File

@ -0,0 +1,41 @@
#encoding: utf-8
module BotpopPlugins
module Intranet
MATCH = lambda do |parent, plugin|
parent.on :message, "!intra" do |m| plugin.exec_intra m end
parent.on :message, "!intra on" do |m| plugin.exec_intra_on m end
parent.on :message, "!intra off" do |m| plugin.exec_intra_off m end
end
HELP = ["!intra <on, off>"]
CONFIG = Botpop::CONFIG['intranet'] || raise(MissingConfigurationZone, 'intranet')
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
def self.exec_intra m
m.reply BotpopBuiltins.intra_state rescue m.reply "I'm buggy. Sorry"
end
INTRA_PING_SLEEP = 30
def self.exec_intra_on m
@intra ||= Mutex.new
if @intra.try_lock
@intra_on = true
m.reply "INTRANET SPY ON"
while @intra_on
m.reply BotpopBuiltins.intra_state rescue return @intra.unlock
sleep INTRA_PING_SLEEP
end
@intra.unlock
else
m.reply "INTRA SPY ALREADY ON"
end
end
def self.exec_intra_off m
m.reply @intra_on ? "INTRA SPY OFF" : "INTRA SPY ALREADY OFF"
@intra_on = false
end
end
end

View File

@ -1,27 +0,0 @@
#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

View File

@ -1,68 +0,0 @@
#encoding: utf-8
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 /.+/, use_prefix: false, method: :exec_log
HELP = ["!log enable", "!log add", "!log remove", "!log users", "!log clean", "!log status"]
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 = CONFIG["default_started"]
def exec_list_user m
m.reply @@log_user_list.join(", ")
m.reply "no log admin" if @@log_user_list.empty?
end
def exec_remove_user m
return unless is_admin? m
m.message.gsub("!log add ", "").split(" ").each do |name|
@@log_user_list.delete name unless USERS["list"].include?(name)
end
end
def exec_add_user m
return unless is_admin? m
@@log_user_list += m.message.gsub("!log add ", "").split(" ")
@@log_user_list.uniq!
end
def exec_log_enable m
@@log_enabled = !@@log_enabled
exec_status m
end
def exec_status m
m.reply "Log #{@@log_enabled ? :enabled : :disabled}"
end
def exec_clean m
return unless is_admin? m
File.delete(CONFIG["file"]) rescue nil
end
def exec_log m
log(m) if @@log_enabled
end
private
def log m
File.open(CONFIG["file"], 'a') {|f| f << (m.user.to_s + ": " + m.message + "\n")}
end
def is_admin? m
@@log_user_list.include? m.user.to_s
end
end

74
plugins/logger.rb Normal file
View File

@ -0,0 +1,74 @@
#encoding: utf-8
module BotpopPlugins
module Logger
NAME = self.to_s.split(':').last
MATCH = lambda do |parent, plugin|
parent.on :message, /!log users/ do |m| plugin.exec_list_user m end
parent.on :message, /!log remove .+/ do |m| plugin.exec_remove_user m end
parent.on :message, /!log add .+/ do |m| plugin.exec_add_user m end
parent.on :message, /!log clean/ do |m| plugin.exec_clean m end
parent.on :message, /!log status/ do |m| plugin.exec_status m end
parent.on :message, /!log\Z/ do |m| plugin.exec_log_enable m end
parent.on :message, /.+/ do |m| plugin.exec_log m end
end
HELP = ["!log", "!log add", "!log remove", "!log users", "!log clean", "!log status"]
CONFIG = Botpop::CONFIG['logger'] || raise(MissingConfigurationZone, NAME)
ENABLED = CONFIG['enable'].nil? ? false : CONFIG['enable']
USER_CONFIG = "plugins/logger_user.yml"
USERS = YAML.load_file(USER_CONFIG) || raise(MissingConfigurationZone, USER_CONFIG)
@@logger_user_list = USERS["list"]
@@logger_enabled = false
def self.exec_list_user m
m.reply @@logger_user_list.join(", ")
m.reply "no logger admin" if @@logger_user_list.empty?
end
def self.exec_remove_user m
return unless is_admin? m
m.message.gsub("!log add ", "").split(" ").each do |name|
@@logger_user_list.delete name unless USERS["list"].include?(name)
end
end
def self.exec_add_user m
return unless is_admin? m
@@logger_user_list += m.message.gsub("!log add ", "").split(" ")
@@logger_user_list.uniq!
end
def self.exec_log_enable m
@@logger_enabled = !@@logger_enabled
exec_status m
end
def self.exec_status m
m.reply "Logger #{@@logger_enabled ? :enabled : :disabled}"
end
def self.exec_clean m
return unless is_admin? m
File.delete(CONFIG["file"]) rescue nil
end
def self.exec_log m
log(m) if @@logger_enabled
end
private
def self.log m
@@logger_lock ||= Mutex.new
@@logger_lock.lock
File.open(CONFIG["file"], 'a') {|f| f << (m.user.to_s + ": " + m.message + "\n")} rescue nil
@@logger_lock.unlock
end
def self.is_admin? m
@@logger_user_list.include? m.user.to_s
end
end
end

View File

@ -1,20 +1,22 @@
#encoding: utf-8 #encoding: utf-8
class Network < Botpop::Plugin module BotpopPlugins
include Cinch::Plugin module Network
match("!ping", use_prefix: false, method: :exec_ping) MATCH = lambda do |parent, plugin|
match(/!ping #{Botpop::TARGET}\Z/, use_prefix: false, method: :exec_ping_target) parent.on :message, "!ping" do |m| plugin.exec_ping m end
match(/!httping #{Botpop::TARGET}\Z/, use_prefix: false, method: :exec_ping_http) parent.on :message, /!ping #{Botpop::TARGET}\Z/ do |m| plugin.exec_ping_target m end
match(/!dos #{Botpop::TARGET}\Z/, use_prefix: false, method: :exec_dos) parent.on :message, /!httping #{Botpop::TARGET}\Z/ do |m| plugin.exec_ping_http m end
match(/!fok #{Botpop::TARGET}\Z/, use_prefix: false, method: :exec_fok) parent.on :message, /!dos #{Botpop::TARGET}\Z/ do |m| plugin.exec_dos m end
match(/!trace #{Botpop::TARGET}\Z/, use_prefix: false, method: :exec_trace) parent.on :message, /!fok #{Botpop::TARGET}\Z/ do |m| plugin.exec_fok m end
match(/!poke #{Botpop::TARGET}\Z/, use_prefix: false, method: :exec_poke) parent.on :message, /!trace #{Botpop::TARGET}\Z/ do |m| plugin.exec_trace m end
parent.on :message, /!poke #{Botpop::TARGET}\Z/ do |m| plugin.exec_poke m end
end
HELP = ["!ping", "!ping [ip]", "!httping [ip]", HELP = ["!ping", "!ping [ip]", "!httping [ip]",
"!dos [ip]", "!fok [nick]", "!trace [ip]", "!poke [nick]"] "!dos [ip]", "!fok [nick]", "!trace [ip]", "!poke [nick]"]
ENABLED = config['enable'].nil? ? true : config['enable'] CONFIG = Botpop::CONFIG['network'] || raise(MissingConfigurationZone, 'network')
CONFIG = config ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
private private
# Conversion of the string to value in ms # Conversion of the string to value in ms
@ -23,59 +25,59 @@ class Network < Botpop::Plugin
end end
public public
DOS_DURATION = config['dos_duration'] || '2s' DOS_DURATION = CONFIG['dos_duration'] || '2s'
DOS_WAIT_DURATION_STRING = config['dos_wait'] || '5s' DOS_WAIT_DURATION_STRING = CONFIG['dos_wait'] || '5s'
DOS_WAIT_DURATION = config_string_to_time DOS_WAIT_DURATION_STRING 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 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_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_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_INIT = config_string_to_time TRACE_DURATION_INIT_STRING
TRACE_DURATION_INCR = config_string_to_time TRACE_DURATION_INCR_STRING TRACE_DURATION_INCR = config_string_to_time TRACE_DURATION_INCR_STRING
# @param what [Net::Ping::External] # @param what [Net::Ping::External]
# @param what [Net::Ping::HTTP] # @param what [Net::Ping::HTTP]
def ping_with m, what def self.ping_with m, what
ip = Botpop::Builtins.get_ip m ip = BotpopBuiltins.get_ip m
p = what.new ip p = what.new ip
str = p.ping(ip) ? "#{(p.duration*100.0).round 2}ms (#{p.host})" : 'failed' str = p.ping(ip) ? "#{(p.duration*100.0).round 2}ms (#{p.host})" : 'failed'
m.reply "#{ip} > #{what.to_s.split(':').last} ping > #{str}" m.reply "#{ip} > #{what.to_s.split(':').last} ping > #{str}"
end end
def exec_ping m def self.exec_ping m
m.reply "#{m.user} > pong" m.reply "#{m.user} > pong"
end end
def exec_ping_target m def self.exec_ping_target m
ping_with m, Net::Ping::External ping_with m, Net::Ping::External
end end
def exec_ping_http m def self.exec_ping_http m
ping_with m, Net::Ping::HTTP ping_with m, Net::Ping::HTTP
end end
def exec_poke m def self.exec_poke m
nick = Botpop::Builtins.get_ip_from_nick(m)[:nick] nick = BotpopBuiltins.get_ip_from_nick(m)[:nick]
ip = Botpop::Builtins.get_ip_from_nick(m)[:ip] ip = BotpopBuiltins.get_ip_from_nick(m)[:ip]
return m.reply "User '#{nick}' doesn't exists" if ip.nil? return m.reply "User '#{nick}' doesn't exists" if ip.nil?
# Display # Display
response = Botpop::Builtins.ping(ip) ? "#{(p.duration*100.0).round 2}ms (#{p.host})" : "failed" response = BotpopBuiltins.ping(ip) ? "#{(p.duration*100.0).round 2}ms (#{p.host})" : "failed"
m.reply "#{nick} > poke > #{response}" m.reply "#{nick} > poke > #{response}"
end end
def dos_check_ip(m, ip) def self.dos_check_ip(m, ip)
return true if Botpop::Builtins.ping(ip) return true if BotpopBuiltins.ping(ip)
m.reply "Cannot reach the host '#{ip}'" m.reply "Cannot reach the host '#{ip}'"
return false return false
end end
def dos_replier m, ip, s def self.dos_replier m, ip, s
if s.nil? if s.nil?
m.reply "The dos has failed" m.reply "The dos has failed"
elsif Botpop::Builtins.ping(ip) elsif BotpopBuiltins.ping(ip)
m.reply "Sorry, the target is still up !" m.reply "Sorry, the target is still up !"
else else
m.reply "Target down ! --- #{s}" m.reply "Target down ! --- #{s}"
@ -88,7 +90,7 @@ class Network < Botpop::Plugin
# releasing the resources and permit a new attack. # releasing the resources and permit a new attack.
# #
# @arg lambda [Lambda] lambda with one argument (m). It wil be executed # @arg lambda [Lambda] lambda with one argument (m). It wil be executed
def dos_execution(m, lambda) def self.dos_execution(m, lambda)
@dos ||= Mutex.new @dos ||= Mutex.new
if @dos.try_lock if @dos.try_lock
lambda.call(m) lambda.call(m)
@ -99,13 +101,13 @@ class Network < Botpop::Plugin
end end
end end
def dos_ip(ip) def self.dos_ip(ip)
return Botpop::Builtins.dos(ip, DOS_DURATION).split("\n")[3].to_s rescue nil return BotpopBuiltins.dos(ip, DOS_DURATION).split("\n")[3].to_s rescue nil
end end
def exec_dos m def self.exec_dos m
dos_execution m, lambda {|m| dos_execution m, lambda {|m|
ip = Botpop::Builtins.get_ip m ip = BotpopBuiltins.get_ip m
return if not dos_check_ip(m, ip) return if not dos_check_ip(m, ip)
m.reply "Begin attack against #{ip}" m.reply "Begin attack against #{ip}"
s = dos_ip(ip) s = dos_ip(ip)
@ -113,19 +115,19 @@ class Network < Botpop::Plugin
} }
end end
def exec_fok m def self.exec_fok m
dos_execution m, lambda {|m| dos_execution m, lambda {|m|
nick = Botpop::Builtins.get_ip_from_nick(m)[:nick] nick = BotpopBuiltins.get_ip_from_nick(m)[:nick]
ip = Botpop::Builtins.get_ip_from_nick(m)[:ip] ip = BotpopBuiltins.get_ip_from_nick(m)[:ip]
return m.reply "User '#{nick}' doesn't exists" if ip.nil? return m.reply "User '#{nick}' doesn't exists" if ip.nil?
return m.reply "Cannot reach the host '#{ip}'" if not Botpop::Builtins.ping(ip) return m.reply "Cannot reach the host '#{ip}'" if not BotpopBuiltins.ping(ip)
s = dos_ip(ip) s = dos_ip(ip)
r = Botpop::Builtins.ping(ip) ? "failed :(" : "down !!!" r = BotpopBuiltins.ping(ip) ? "failed :(" : "down !!!"
m.reply "#{nick} : #{r} #{s}" m.reply "#{nick} : #{r} #{s}"
} }
end end
def trace_display_lines m, lines def self.trace_display_lines m, lines
lines.select!{|e| not e.include? "no reply" and e =~ /\A \d+: .+/} lines.select!{|e| not e.include? "no reply" and e =~ /\A \d+: .+/}
duration = TRACE_DURATION_INIT duration = TRACE_DURATION_INIT
lines.each do |l| lines.each do |l|
@ -136,9 +138,9 @@ class Network < Botpop::Plugin
m.reply 'finished' m.reply 'finished'
end end
def trace_with_time ip def self.trace_with_time ip
t1 = Time.now t1 = Time.now
s = Botpop::Builtins.trace ip s = BotpopBuiltins.trace ip
t2 = Time.now t2 = Time.now
return [s, t1, t2] return [s, t1, t2]
end end
@ -146,7 +148,7 @@ class Network < Botpop::Plugin
# see {trace_execution}. Seem system without sleep # see {trace_execution}. Seem system without sleep
# #
# @arg lambda [Lambda] lambda with one argument (m). It wil be executed # @arg lambda [Lambda] lambda with one argument (m). It wil be executed
def trace_execution(m, lambda) def self.trace_execution(m, lambda)
@trace ||= Mutex.new @trace ||= Mutex.new
if @trace.try_lock if @trace.try_lock
lambda.call(m) rescue nil lambda.call(m) rescue nil
@ -156,9 +158,9 @@ class Network < Botpop::Plugin
end end
end end
def exec_trace m def self.exec_trace m
trace_execution m, lambda {|m| trace_execution m, lambda {|m|
ip = Botpop::Builtins.get_ip m ip = BotpopBuiltins.get_ip m
m.reply "It can take time" m.reply "It can take time"
begin begin
# Calculations # Calculations
@ -173,3 +175,4 @@ class Network < Botpop::Plugin
end end
end end
end

View File

@ -1,51 +0,0 @@
#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

View File

@ -1,17 +0,0 @@
# 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

View File

@ -1,76 +0,0 @@
#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

View File

@ -1,12 +0,0 @@
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

View File

@ -1 +0,0 @@
base/migrations/

View File

@ -1,49 +1,52 @@
#encoding: utf-8 #encoding: utf-8
trap('SIGINT') { trap('SIGINT') {
Botpop::Plugins::Proxy.database_users_write({}) BotpopPlugins::Proxy.database_users_write({})
exit exit
} }
class Proxy < Botpop::Plugin module BotpopPlugins
include Cinch::Plugin module Proxy
match("!proxy list", use_prefix: false, method: :exec_proxy_list) MATCH = lambda do |parent, plugin|
match("!proxy ip", use_prefix: false, method: :exec_proxy_ip) parent.on :message, "!proxy list" do |m| plugin.exec_proxy_list m end
match("!proxy get", use_prefix: false, method: :exec_proxy_get) parent.on :message, "!proxy ip" do |m| plugin.exec_proxy_ip m end
match("!proxy drop", use_prefix: false, method: :exec_proxy_drop) parent.on :message, "!proxy get" do |m| plugin.exec_proxy_get m end
parent.on :message, "!proxy drop" do |m| plugin.exec_proxy_drop m end
HELP = ["!proxy list", "!proxy ip", "!proxy get", "!proxy drop"] end
ENABLED = config['enable'].nil? ? true : config['enable'] HELP = ["!proxy list", "!proxy ip",
"!proxy get", "!proxy drop"]
CONFIG = Botpop::CONFIG['proxy'] || raise(MissingConfigurationZone, 'proxy')
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
if ENABLED if ENABLED
require 'htauth' require 'htauth'
require 'digest' require 'digest'
end end
LIMIT_USERS = config['limit_users'] || 1 LIMIT_USERS = CONFIG['limit_users'] || 1
PASSWD_FILE = config['passwd_file'] || '/etc/squid3/passwords' PASSWD_FILE = CONFIG['passwd_file'] || '/etc/squid3/passwords'
IP = config['ip_addr'] || raise(MissingConfigurationEntry, 'ip_addr') IP = CONFIG['ip_addr'] || raise(MissingConfigurationEntry, 'ip_addr')
PORT = config['ip_port'] || raise(MissingConfigurationEntry, 'ip_port') PORT = CONFIG['ip_port'] || raise(MissingConfigurationEntry, 'ip_port')
File.open(PASSWD_FILE, 'a') {} File.open(PASSWD_FILE, 'a') {}
LOCKED_USERS = File.readlines(PASSWD_FILE) LOCKED_USERS = File.readlines(PASSWD_FILE)
@@proxy_users = [] @@proxy_users = []
def username m def self.username m
Digest::SHA256.hexdigest m.user.nick Digest::SHA256.hexdigest m.user.nick
end end
def password_rand def self.password_rand
File.readlines('/proc/sys/kernel/random/uuid').first.split('-').last.chomp File.readlines('/proc/sys/kernel/random/uuid').first.split('-').last.chomp
end end
def database_users_reset def self.database_users_reset
File.write(PASSWD_FILE, '') File.write(PASSWD_FILE, '')
end end
def database_users_read def self.database_users_read
begin begin
return File.readlines(PASSWD_FILE) return File.readlines(PASSWD_FILE)
rescue rescue
@ -53,14 +56,14 @@ class Proxy < Botpop::Plugin
end end
end end
def database_users_write users def self.database_users_write users
database_users_reset database_users_reset
contents = (LOCKED_USERS + users.map{|u,p| "#{u}:#{p}"}).join("\n").chomp contents = (LOCKED_USERS + users.map{|u,p| "#{u}:#{p}"}).join("\n").chomp
contents += "\n" if not contents.empty? contents += "\n" if not contents.empty?
File.write(PASSWD_FILE, contents) File.write(PASSWD_FILE, contents)
end end
def users def self.users
users = database_users_read users = database_users_read
users.map!{|l| l.chomp.split(':')} users.map!{|l| l.chomp.split(':')}
users.map!{|u| {u[0] => u[1]}} users.map!{|u| {u[0] => u[1]}}
@ -68,34 +71,34 @@ class Proxy < Botpop::Plugin
users users
end end
def remove_user nick def self.remove_user nick
users = database_users_read users = database_users_read
users.delete_if {|line| line =~ /\A#{nick}:.+/ } users.delete_if {|line| line =~ /\A#{nick}:.+/ }
database_users_write users database_users_write users
end end
def add_user username, password def self.add_user username, password
p = HTAuth::PasswdFile.new(PASSWD_FILE) p = HTAuth::PasswdFile.new(PASSWD_FILE)
p.add(username, password) p.add(username, password)
p.save! p.save!
end end
def user_exists? m def self.user_exists? m
users[username m] users[username m]
end end
def exec_proxy_list m def self.exec_proxy_list m
users.keys.each_with_index do |username, i| users.keys.each_with_index do |username, i|
m.reply "Proxy list [#{i+1}/#{users.size}/#{LIMIT_USERS}] : #{username}" m.reply "Proxy list [#{i+1}/#{users.size}/#{LIMIT_USERS}] : #{username}"
sleep 0.1 sleep 0.1
end end
end end
def exec_proxy_ip m def self.exec_proxy_ip m
m.reply "Proxy connexion on http://#{IP}:#{PORT}" m.reply "Proxy connexion on http://#{IP}:#{PORT}"
end end
def exec_proxy_get m def self.exec_proxy_get m
if not user_exists? m if not user_exists? m
password = password_rand password = password_rand
@@proxy_users << m.user.nick @@proxy_users << m.user.nick
@ -110,7 +113,7 @@ class Proxy < Botpop::Plugin
end end
end end
def exec_proxy_drop m def self.exec_proxy_drop m
if @@proxy_users.include?(m.user.nick) and user_exists? m if @@proxy_users.include?(m.user.nick) and user_exists? m
@@proxy_users.delete(m.user.nick) @@proxy_users.delete(m.user.nick)
remove_user(username(m)) remove_user(username(m))
@ -121,3 +124,4 @@ class Proxy < Botpop::Plugin
end end
end end
end

View File

@ -1,119 +0,0 @@
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

View File

@ -1,42 +0,0 @@
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

View File

@ -1,16 +1,20 @@
#encoding: utf-8 #encoding: utf-8
class SayGoodBye < Botpop::Plugin module BotpopPlugins
include Cinch::Plugin module SayGoodByePlugin
NAME = self.to_s.split(':').last
match(/^!sg [\w\-\.].+/, use_prefix: false, method: :exec_sg)
MATCH = lambda do |parent, plugin|
parent.on :message, /!sg [\w\d_\-\.].+/ do |m| plugin.exec_sg m end
end
HELP = ["!sg src_name"] HELP = ["!sg src_name"]
ENABLED = config['enable'].nil? ? true : config['enable'] CONFIG = Botpop::CONFIG['say_goodbye'] || raise(MissingConfigurationZone, NAME)
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
def exec_sg m def self.exec_sg m
arg = m.message.split.last arg = m.message.split.last
m.reply config[arg].sample m.reply CONFIG[arg].shuffle.first
end end
end end
end

View File

@ -1,21 +1,24 @@
#encoding: utf-8 #encoding: utf-8
class Searchable < Botpop::Plugin module BotpopPlugins
include Cinch::Plugin module Searchable
ENABLED = config['enable'].nil? ? true : config['enable'] MATCH = lambda do |parent, plugin|
parent.on :message, /\!(#{CONFIG.keys.join('|')}) .+/ do |m| plugin.exec_search m end
end
CONFIG = Botpop::CONFIG['searchable'] || raise(MissingConfigurationZone, 'base')
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable']
VALUES = config.values.map{|e|"!"+e}.join(', ') VALUES = CONFIG.values.map{|e|"!"+e}.join(', ')
KEYS = config.keys.map{|e|"!"+e}.join(', ') KEYS = CONFIG.keys.map{|e|"!"+e}.join(', ')
HELP = config.keys.map{|e|"!"+e+" [search]"} HELP = CONFIG.keys.map{|e|"!"+e+" [search]"}
CONFIG = config
match(/\!(#{config.keys.join('|')}) .+/, use_prefix: false, method: :exec_search)
def exec_search m def self.exec_search m
msg = Botpop::Builtins.get_msg m msg = BotpopBuiltins.get_msg m
url = CONFIG[m.params[1..-1].join(' ').gsub(/\!([^ ]+) .+/, '\1')] url = CONFIG[m.params[1..-1].join(' ').gsub(/\!([^ ]+) .+/, '\1')]
url = url.gsub('___MSG___', msg) url = url.gsub('___MSG___', msg)
m.reply url m.reply url
end end
end end
end

View File

@ -1,32 +1,31 @@
#encoding: utf-8 #encoding: utf-8
class Taggle < Botpop::Plugin module BotpopPlugins
include Cinch::Plugin module Taggle
NAME = self.to_s.split(':').last
match(/!tg (.+)/, use_prefix: false, method: :exec_tg)
MATCH = lambda do |parent, plugin|
# Self is the callback, containing User()
parent.on :message, /!tg (.+)/ do |m, who| plugin.exec_tg self, m, who end
end
HELP = ["!tg [nick]"] HELP = ["!tg [nick]"]
CONFIG = config(safe: true) || {} CONFIG = Botpop::CONFIG['taggle'] || {} # || raise(MissingConfigurationZone, NAME)
ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable'] ENABLED = CONFIG['enable'].nil? ? true : CONFIG['enable'] rescue true
NTIMES = CONFIG['ntimes'] || 10 NTIMES = CONFIG['ntimes'] || 10
WAIT = CONFIG['wait'] || 0.3 WAIT = CONFIG['wait'] || 0.3
def cmd_allowed? m def self.exec_tg c, m, who
return Base.cmd_allowed? m, ["tg"] @tg_lock ||= Mutex.new
end @tg_lock.lock
def exec_tg m, who
return if not cmd_allowed? m
@@tg_lock ||= Mutex.new
@@tg_lock.lock
begin begin
NTIMES.times do NTIMES.times do
User(who).send("tg #{who}") c.User(who).send("tg #{who}")
sleep WAIT sleep WAIT
end end
ensure ensure
@@tg_lock.unlock @tg_lock.unlock
end end
end end
end end
end

View File

@ -1,36 +0,0 @@
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

View File

@ -17,8 +17,8 @@ class TestBotbot < Test::Unit::TestCase
assert(Botpop::CONFIG) assert(Botpop::CONFIG)
assert(Botpop::TARGET) assert(Botpop::TARGET)
assert(Botpop::PluginInclusion.class == Module) assert(Botpop::PluginInclusion.class == Module)
assert(Botpop::Builtins.class == Module) assert(BotpopBuiltins.class == Module)
assert(Botpop::Plugin.class == Class) assert(BotpopPlugins.class == Module)
end end
end end

View File

@ -1 +1 @@
v1.2-4 v0.12-1 Ohmygodxdlol