cogbot 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ .bundle
3
+ config/cogbot.yml
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm_gemset_create_on_use_flag=1
2
+ rvm gemset use cogbot
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
data/Gemfile.lock ADDED
@@ -0,0 +1,74 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cogbot (0.0.2)
5
+ cinch
6
+ daemons
7
+ eventmachine
8
+ eventmachine_httpserver
9
+ fortune_gem
10
+ json
11
+ nokogiri
12
+ thor
13
+ twitter
14
+ yajl-ruby
15
+
16
+ GEM
17
+ remote: https://rubygems.org/
18
+ specs:
19
+ awesome_print (1.1.0)
20
+ cinch (2.0.4)
21
+ coderay (1.0.9)
22
+ daemons (1.1.9)
23
+ eventmachine (1.0.3)
24
+ eventmachine_httpserver (0.2.1)
25
+ faraday (0.8.7)
26
+ multipart-post (~> 1.1)
27
+ flog (4.0.0)
28
+ ruby_parser (~> 3.1, > 3.1.0)
29
+ sexp_processor (~> 4.0)
30
+ fortune_gem (0.0.6)
31
+ json (1.7.7)
32
+ method_source (0.8.1)
33
+ minitest (4.7.4)
34
+ minitest-ci (2.4.0)
35
+ minitest (>= 3.0, < 5.0)
36
+ multi_json (1.7.2)
37
+ multipart-post (1.2.0)
38
+ nokogiri (1.5.9)
39
+ pry (0.9.12.1)
40
+ coderay (~> 1.0.5)
41
+ method_source (~> 0.8)
42
+ slop (~> 3.4)
43
+ rake (10.0.4)
44
+ ruby_parser (3.1.3)
45
+ sexp_processor (~> 4.1)
46
+ sexp_processor (4.2.1)
47
+ simple_oauth (0.2.0)
48
+ simplecov (0.7.1)
49
+ multi_json (~> 1.0)
50
+ simplecov-html (~> 0.7.1)
51
+ simplecov-html (0.7.1)
52
+ simplecov-rcov (0.2.3)
53
+ simplecov (>= 0.4.1)
54
+ slop (3.4.4)
55
+ thor (0.18.1)
56
+ twitter (4.6.2)
57
+ faraday (~> 0.8, < 0.10)
58
+ multi_json (~> 1.0)
59
+ simple_oauth (~> 0.2)
60
+ yajl-ruby (1.1.0)
61
+
62
+ PLATFORMS
63
+ ruby
64
+
65
+ DEPENDENCIES
66
+ awesome_print
67
+ cogbot!
68
+ flog
69
+ minitest
70
+ minitest-ci
71
+ pry
72
+ rake
73
+ simplecov
74
+ simplecov-rcov
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 mose
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Cogbot
2
+
3
+
4
+
5
+ Cogbot is an irc bot written in ruby based on [Cinch bot framework](https://github.com/cinchrb/cinch).
6
+
7
+ It has been in servvice at [Code Green](http://codegreenit.com) since summer 2012 and his stability
8
+ is unquestionned. It is used with a collection of custom plugins that are focused on helping a coding
9
+ team that uses irc as a main shared communication space:
10
+
11
+ * git notifications pushed on the channel
12
+ * redmine issues polled from our redmine and announced
13
+ * commands to ask rubygems or stack overflow
14
+ * the urban dictionary to make us laugh
15
+ * and some other more or less used features
16
+
17
+ ## Installation
18
+
19
+ gem install cogbot
20
+
21
+ ## Usage
22
+
23
+ At first launch:
24
+
25
+ cogbot start
26
+
27
+ you will be prompted to create a configuration file in ~/.cogbot/cogbot.yml
28
+ When this is done you can launch again and it will just run according to your configuration.
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create new Pull Request
37
+
38
+ ## Todo
39
+
40
+ * write some documentation of the commands of plugins
41
+ * write tests
42
+
43
+ ## Licence
44
+
45
+ MIT license
46
+
47
+ Copyright (c) 2012-13 mose at Code Green
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs = ["lib","plugins"]
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :ci_cleanup do
11
+ require 'minitest/ci'
12
+ MiniTest::Ci.clean
13
+ end
14
+
15
+ task :default => :test
data/bin/cogbot ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ROOT_DIR = File.expand_path('../..', __FILE__)
4
+ $LOAD_PATH.unshift(ROOT_DIR)
5
+ CONFIG_DIR = File.join(File.expand_path('~'),'.cogbot')
6
+ CONFIG_FILE = File.join(CONFIG_DIR,'cogbot.yml')
7
+ LOG_DIR = File.join(CONFIG_DIR,'logs')
8
+
9
+ require 'yaml'
10
+ require 'lib/cogbot'
11
+
12
+ Cogbot::Bot.start
data/cogbot.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/cogbot/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["mose"]
6
+ gem.email = ["mose@mose.com"]
7
+ gem.description = 'Irc bot based on Cinch'
8
+ gem.summary = 'Yet another irc bot, focused on helping development teams.'
9
+ gem.homepage = "https://github.com/mose/cogbot"
10
+ gem.license = "MIT"
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = ['cogbot']
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "cogbot"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Cogbot::VERSION
18
+
19
+ gem.add_dependency 'cinch'
20
+ gem.add_dependency "thor"
21
+ gem.add_dependency "eventmachine"
22
+ gem.add_dependency "eventmachine_httpserver"
23
+ gem.add_dependency 'nokogiri'
24
+ gem.add_dependency "daemons"
25
+
26
+ gem.add_dependency 'twitter' # twitter plugin
27
+ gem.add_dependency 'json' # stackoverflow plugin
28
+ gem.add_dependency 'yajl-ruby' # rubygems plugin
29
+ gem.add_dependency 'fortune_gem' # fortune plugin
30
+
31
+ gem.add_development_dependency 'awesome_print'
32
+ gem.add_development_dependency 'rake'
33
+ gem.add_development_dependency 'minitest'
34
+ gem.add_development_dependency 'minitest-ci'
35
+ gem.add_development_dependency 'pry'
36
+ gem.add_development_dependency 'simplecov'
37
+ gem.add_development_dependency 'simplecov-rcov'
38
+ gem.add_development_dependency 'flog'
39
+ end
@@ -0,0 +1,26 @@
1
+ main:
2
+ server: 'irc.freenode.net'
3
+ port: '6667'
4
+ ssl: false
5
+ nick: 'cogbot'
6
+ channels:
7
+ - '#cinch-bots'
8
+ prefix: '.'
9
+ plugins:
10
+ - 'manager'
11
+ - 'google'
12
+ - 'dice'
13
+ - 'urban'
14
+ - 'tweet'
15
+ server:
16
+ ip: 127.0.0.1
17
+ port: 9090
18
+ tweet:
19
+ consumer_key: ''
20
+ consumer_secret: ''
21
+ oauth_token: ''
22
+ oauth_token_secret: ''
23
+ redmine:
24
+ api_key: ''
25
+ url: ''
26
+ project: ''
@@ -0,0 +1,22 @@
1
+ class Server < EM::Connection
2
+ include EM::HttpServer
3
+
4
+ def initialize(bot)
5
+ @bot = bot
6
+ end
7
+
8
+ def post_init
9
+ super
10
+ no_environment_strings
11
+ end
12
+
13
+ def process_http_request
14
+ if @http_request_method == "POST"
15
+ @bot.handlers.dispatch(:api_callback, nil, @http_post_content)
16
+ end
17
+
18
+ response = EM::DelegatedHttpResponse.new(self)
19
+ response.status = 200
20
+ response.send_response
21
+ end
22
+ end
@@ -0,0 +1,70 @@
1
+ module Cogbot
2
+ module Setup
3
+
4
+ def self.setvalue default
5
+ input = $stdin.gets.chomp
6
+ if input == ''
7
+ default
8
+ else
9
+ input
10
+ end
11
+ end
12
+
13
+ def self.setbinary default
14
+ input = $stdin.gets.chomp
15
+ if input == ''
16
+ default
17
+ else
18
+ input.downcase == 'y'
19
+ end
20
+ end
21
+
22
+ def self.setlist default
23
+ input = $stdin.gets.chomp
24
+ if input == ''
25
+ default
26
+ else
27
+ input.split(',')
28
+ end
29
+ end
30
+
31
+ def self.init
32
+ st = "\033[0;33m"
33
+ en = "\033[m"
34
+
35
+ default = YAML::load_file(File.join(ROOT_DIR,'config','cogbot.yml.defaults'))
36
+ main = {}
37
+ puts "You don't have a configuration file yet,"
38
+ puts "let's make one in ~/.cogbot/config.yml"
39
+ puts
40
+ puts "Please answer those few questions"
41
+
42
+ print "#{st}What name will cogbot use ?#{en} [#{default['main']['nick']}] "
43
+ main['nick'] = setvalue default['main']['nick']
44
+
45
+ print "#{st}What irc server #{main['nick']} will connect to ?#{en} [#{default['main']['server']}] "
46
+ main['server'] = setvalue default['main']['server']
47
+
48
+ print "#{st}What port #{main['nick']} will use to connect to #{main['server']} ?#{en} [#{default['main']['port']}] "
49
+ main['port'] = setvalue default['main']['port']
50
+
51
+ print "#{st}Will #{main['nick']} use SSL to connect to #{main['server']}:#{main['port']} ?#{en} [#{default['main']['ssl'] ? 'Yn' : 'yN'}] "
52
+ main['ssl'] = setbinary default['main']['ssl']
53
+
54
+ print "#{st}What channels #{main['nick']} should join ?#{en} [#{default['main']['channels'].join(',')}] "
55
+ main['channels'] = setlist default['main']['channels']
56
+
57
+ print "#{st}What prefix #{main['nick']} will use to understand he's talked to ?#{en} [#{default['main']['prefix']}] "
58
+ main['prefix'] = setvalue default['main']['prefix']
59
+
60
+ print "#{st}What plugins will be enabled for #{main['nick']} ?#{en} [#{default['main']['plugins'].join(',')}] "
61
+ main['plugins'] = setlist default['main']['plugins']
62
+
63
+ FileUtils.mkdir_p(CONFIG_DIR) unless File.directory?(CONFIG_DIR)
64
+ FileUtils.mkdir_p(LOG_DIR) unless File.directory?(LOG_DIR)
65
+ File.open(CONFIG_FILE,'w') { |f| YAML::dump({ 'main' => main }, f) }
66
+ return main
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,9 @@
1
+ class String
2
+
3
+ def camelize
4
+ self.downcase!
5
+ self.gsub!(/(?:_| )?(.)([^_ ]*)/) { "#{$1.upcase}#{$2}" }
6
+ end
7
+
8
+ end
9
+
@@ -0,0 +1,3 @@
1
+ module Cogbot
2
+ VERSION = "0.0.2"
3
+ end
data/lib/cogbot.rb ADDED
@@ -0,0 +1,106 @@
1
+ require 'cinch'
2
+ require 'eventmachine'
3
+ require 'evma_httpserver'
4
+ require 'nokogiri'
5
+ require 'net/http'
6
+ require 'daemons'
7
+ require 'thor'
8
+ require 'yajl'
9
+ #require 'cinch/storage/yaml'
10
+ require "lib/cogbot/version"
11
+ require "lib/cogbot/utils"
12
+ require "lib/cogbot/server"
13
+
14
+ module Cogbot
15
+
16
+ class Bot < Thor
17
+
18
+ desc "start", "start cogbot"
19
+ def start
20
+
21
+ # prepare config
22
+ config = {}
23
+ begin
24
+ config = YAML::load_file(CONFIG_FILE)
25
+ rescue Exception => e
26
+ load "lib/cogbot/setup.rb"
27
+ config['main'] = Cogbot::Setup.init
28
+ end
29
+
30
+ # prepare daemon
31
+ Daemons.daemonize(
32
+ :app_name => 'cogbot',
33
+ :dir_mode => :normal,
34
+ :log_dir => LOG_DIR,
35
+ :log_output => true,
36
+ :dir => File.join('/', 'tmp')
37
+ )
38
+
39
+ # checkout plugins
40
+ plugins = []
41
+ config['main']['plugins'].each do |p|
42
+ puts p
43
+ puts File.join(ROOT_DIR,'plugins',"#{p}.rb")
44
+ if File.exists?(File.join(ROOT_DIR,'plugins',"#{p}.rb"))
45
+ puts p
46
+ require File.join(ROOT_DIR,'plugins',p)
47
+ plugins.push Cinch::Plugins.const_get(p.camelize)
48
+ end
49
+ end
50
+
51
+ # create bot
52
+ bot = Cinch::Bot.new do
53
+ configure do |c|
54
+ c.server = config['main']['server']
55
+ c.ssl.use = ( config['main']['ssl'] == 'true' )
56
+ c.nick = config['main']['nick']
57
+ c.user = config['main']['nick']
58
+ c.realname = config['main']['nick']
59
+ c.channels = config['main']['channels']
60
+ c.options = { 'cogconf' => config }
61
+ c.plugins.prefix = config['main']['prefix']
62
+ c.plugins.plugins = plugins
63
+ #c.storage.backend = Cinch::Storage::YAML
64
+ #c.storage.basedir = File.join(ROOT_DIR,"yaml")
65
+ #c.storage.autosave = true
66
+ end
67
+ on :message, "hi" do |m|
68
+ m.reply "Hello, #{m.user.nick}"
69
+ end
70
+ end
71
+ bot.loggers.debug(plugins.inspect)
72
+
73
+ Signal.trap('TERM') { EM.stop }
74
+
75
+ EM.run do
76
+ EM.defer { bot.start }
77
+ if config['server']
78
+ EM.add_timer(3) do
79
+ EM.start_server(
80
+ config['server']['ip'],
81
+ config['server']['port'],
82
+ Server,
83
+ bot
84
+ )
85
+ end
86
+ end
87
+ end
88
+
89
+ bot.quit
90
+ end
91
+
92
+ desc "stop", "stop cogbot"
93
+ def stop
94
+ pid_file = File.join('/', 'tmp', 'cogbot.pid')
95
+ pid = File.read(pid_file).to_i if File.exist?(pid_file)
96
+ Process.kill('TERM', pid)
97
+ end
98
+
99
+ desc "restart", "restart cogbot"
100
+ def restart
101
+ stop
102
+ start
103
+ end
104
+
105
+ end
106
+ end
data/plugins/dice.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Cinch
2
+ module Plugins
3
+ class Dice
4
+ include Cinch::Plugin
5
+
6
+ match /^roll$/
7
+
8
+ set :plugin_name, 'dice'
9
+ set :help, <<EOT
10
+ .roll Rolls a dice.
11
+ EOT
12
+
13
+ def new(bot)
14
+ @bot = bot
15
+ end
16
+
17
+ def execute(m)
18
+ score = rand(100) + 1
19
+ m.reply("#{m.user.nick} rolls #{score}")
20
+ end
21
+
22
+ end
23
+ end
24
+ end
data/plugins/exec.rb ADDED
@@ -0,0 +1,56 @@
1
+ module Cinch
2
+ module Plugins
3
+ class Exec
4
+ include Cinch::Plugin
5
+
6
+ match /do ([^ ]*)( ?.*)/
7
+
8
+ set :plugin_name, 'exec'
9
+ set :help, <<EOT
10
+ Exec is a commandline execution proxy: do <command> <pipe-grep> (only the 5 first lines will be displayed)
11
+ .do psaux : rnning processes
12
+ .do df : Disk free
13
+ .do last : last logged
14
+ .do free : free mem
15
+ .do who : who is logged
16
+ .do uptime : uptime and load average
17
+ EOT
18
+
19
+ def new(bot)
20
+ @bot = bot
21
+ end
22
+
23
+ def exec(command,args)
24
+ commands = {
25
+ 'psaux' => 'ps -aux',
26
+ 'df' => 'df -h',
27
+ 'last' => 'last',
28
+ 'free' => 'free -mol',
29
+ 'who' => 'w -shuo',
30
+ 'f' => 'fortune',
31
+ 'uptime' => 'uptime'
32
+ }
33
+ if command == 'help'
34
+ return commands.inspect
35
+ elsif !commands[command].nil?
36
+ args.strip!
37
+ line = commands[command]
38
+ unless args.nil? || args == ''
39
+ line += ' | grep ' + args.gsub(/;|&/,'')
40
+ end
41
+ line += '| head -5'
42
+ #back = line + "\n"
43
+ back = `#{line}`
44
+ return back
45
+ else
46
+ return "Command #{command} not found"
47
+ end
48
+ end
49
+
50
+ def execute(m,command,args)
51
+ m.reply(exec(command,args))
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,32 @@
1
+ require "fortune_gem"
2
+
3
+ module Cinch
4
+ module Plugins
5
+ class Fortune
6
+ include Cinch::Plugin
7
+
8
+ match /f$/
9
+
10
+ set :plugin_name, 'fortune'
11
+ set :help, <<EOT
12
+ .f gives you a bit of random wisdom.
13
+ EOT
14
+
15
+ def new(bot)
16
+ @bot = bot
17
+ end
18
+
19
+ def fortune
20
+ max_length = 256
21
+ fortune = FortuneGem.give_fortune(:max_length => max_length)
22
+ fortune.gsub(/[\n]/, "").gsub(/[\t]/, " ").gsub("?A:", "? A:")
23
+ end
24
+
25
+ def execute(m)
26
+ m.reply(fortune)
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,38 @@
1
+ require "cgi"
2
+
3
+ module Cinch
4
+ module Plugins
5
+ class Gitlistener
6
+ include Cinch::Plugin
7
+
8
+ set :plugin_name, 'gitlistener'
9
+
10
+ listen_to :api_callback
11
+
12
+ def listen(m, json)
13
+ hash = Yajl::Parser.parse(URI.unescape(json[8..-1]))
14
+ bot.loggers.debug(hash.inspect)
15
+ @bot.config.options['cogconf']['main']['channels'].each do |channel|
16
+ #['#dev'].each do |channel|
17
+ hash['commits'].each do |c|
18
+ files = ""
19
+ files += '[= ' + short(c['modified']).join(' ') + ' ] ' unless c['modified'].empty?
20
+ files += '[- ' + short(c['removed']).join(' ') + ' ] ' unless c['removed'].empty?
21
+ files += '[+ ' + short(c['added']).join(' ') + ' ] ' unless c['added'].empty?
22
+ Channel(channel).msg "%s@%s: %s %s" % [
23
+ c['committer']['name'],
24
+ hash['repository']['name'],
25
+ CGI::unescape(c['message']),
26
+ files
27
+ ]
28
+ end
29
+ end
30
+ end
31
+
32
+ def short(x)
33
+ x.map { |l| l.gsub(/([^\/]*)\//) { |s| s[0,1] + '/'} }
34
+ end
35
+
36
+ end
37
+ end
38
+ end
data/plugins/google.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'cgi'
4
+
5
+ module Cinch
6
+ module Plugins
7
+ class Google
8
+ include Cinch::Plugin
9
+
10
+ match /g (.+)/
11
+
12
+ set :plugin_name, 'google'
13
+ set :help, <<EOT
14
+ Google returns the first google match on a search
15
+ .g <keywords> : searches on those keywords
16
+ EOT
17
+
18
+ def new(bot)
19
+ @bot = bot
20
+ end
21
+
22
+ def search(query)
23
+ url = "http://www.google.com/search?q=#{CGI.escape(query)}"
24
+ res = Nokogiri::HTML(open(url)).at("h3.r")
25
+
26
+ title = res.text
27
+ link = res.at('a')[:href]
28
+ link.gsub!(/^.*q=(.*)&sa.*$/) { $1 }
29
+ desc = res.at("./following::div").children.first.text
30
+ CGI.unescape_html "#{title} - #{desc} (#{link})"
31
+ rescue
32
+ "No results found"
33
+ end
34
+
35
+ def execute(m, query)
36
+ m.reply(search(query))
37
+ end
38
+ end
39
+ end
40
+ end