cogbot 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rvmrc +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +74 -0
- data/LICENSE +22 -0
- data/README.md +47 -0
- data/Rakefile +15 -0
- data/bin/cogbot +12 -0
- data/cogbot.gemspec +39 -0
- data/config/cogbot.yml.defaults +26 -0
- data/lib/cogbot/server.rb +22 -0
- data/lib/cogbot/setup.rb +70 -0
- data/lib/cogbot/utils.rb +9 -0
- data/lib/cogbot/version.rb +3 -0
- data/lib/cogbot.rb +106 -0
- data/plugins/dice.rb +24 -0
- data/plugins/exec.rb +56 -0
- data/plugins/fortune.rb +32 -0
- data/plugins/gitlistener.rb +38 -0
- data/plugins/google.rb +40 -0
- data/plugins/manager.rb +102 -0
- data/plugins/redmine.rb +61 -0
- data/plugins/rubygems.rb +51 -0
- data/plugins/shorturl.rb +32 -0
- data/plugins/stackoverflow.rb +37 -0
- data/plugins/tests.rb +18 -0
- data/plugins/tweet.rb +54 -0
- data/plugins/urban.rb +36 -0
- data/plugins/users.rb +39 -0
- data/test/helper.rb +13 -0
- metadata +372 -0
data/plugins/manager.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# thx dominikh for this code :) (then slightly modified)
|
2
|
+
module Cinch
|
3
|
+
module Plugins
|
4
|
+
class Manager
|
5
|
+
include Cinch::Plugin
|
6
|
+
|
7
|
+
match(/m list/, method: :list_plugins)
|
8
|
+
match(/m load (\S+)/, method: :load_plugin)
|
9
|
+
match(/m unload (\S+)/, method: :unload_plugin)
|
10
|
+
match(/m reload (\S+)/, method: :reload_plugin)
|
11
|
+
match(/m set (\S+) (\S+) (.+)$/, method: :set_option)
|
12
|
+
|
13
|
+
def list_plugins(m)
|
14
|
+
back = ''
|
15
|
+
@bot.plugins.each { |p| back += "#{p.class.name.split('::').last.downcase} " }
|
16
|
+
m.reply back
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_plugin(m, mapping)
|
20
|
+
plugin = mapping.downcase.camelize
|
21
|
+
p plugin
|
22
|
+
p mapping
|
23
|
+
|
24
|
+
file_name = "#{ROOT_DIR}/plugins/#{mapping.downcase}.rb"
|
25
|
+
unless File.exist?(file_name)
|
26
|
+
m.reply "Could not load #{plugin} because #{file_name} does not exist."
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
load(file_name)
|
32
|
+
rescue Exception
|
33
|
+
m.reply "Could not load #{plugin}."
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
const = Cinch::Plugins.const_get(plugin)
|
39
|
+
rescue NameError
|
40
|
+
m.reply "Could not load #{plugin} because no matching class was found."
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
@bot.plugins.register_plugin(const)
|
45
|
+
m.reply "Successfully loaded #{plugin}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def unload_plugin(m, plugin)
|
49
|
+
begin
|
50
|
+
plugin_class = Cinch::Plugins.const_get(plugin.downcase.camelize)
|
51
|
+
rescue NameError
|
52
|
+
m.reply "Could not unload #{plugin} because no matching class was found."
|
53
|
+
return
|
54
|
+
end
|
55
|
+
|
56
|
+
@bot.plugins.select {|p| p.class == plugin_class}.each do |p|
|
57
|
+
@bot.plugins.unregister_plugin(p)
|
58
|
+
end
|
59
|
+
|
60
|
+
## FIXME not doing this at the moment because it'll break
|
61
|
+
## plugin options. This means, however, that reloading a
|
62
|
+
## plugin is relatively dirty: old methods will not be removed
|
63
|
+
## but only overwritten by new ones. You will also not be able
|
64
|
+
## to change a classes superclass this way.
|
65
|
+
# Cinch::Plugins.__send__(:remove_const, plugin)
|
66
|
+
|
67
|
+
# Because we're not completely removing the plugin class,
|
68
|
+
# reset everything to the starting values.
|
69
|
+
plugin_class.hooks.clear
|
70
|
+
plugin_class.matchers.clear
|
71
|
+
plugin_class.listeners.clear
|
72
|
+
plugin_class.timers.clear
|
73
|
+
plugin_class.ctcps.clear
|
74
|
+
plugin_class.react_on = :message
|
75
|
+
plugin_class.plugin_name = nil
|
76
|
+
plugin_class.help = nil
|
77
|
+
plugin_class.prefix = nil
|
78
|
+
plugin_class.suffix = nil
|
79
|
+
plugin_class.required_options.clear
|
80
|
+
|
81
|
+
m.reply "Successfully unloaded #{plugin}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def reload_plugin(m, plugin)
|
85
|
+
unload_plugin(m, plugin)
|
86
|
+
load_plugin(m, plugin)
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_option(m, plugin, option, value)
|
90
|
+
begin
|
91
|
+
const = Cinch::Plugins.const_get(plugin.downcase.camelize)
|
92
|
+
rescue NameError
|
93
|
+
m.reply "Could not set plugin option for #{plugin} because no matching class was found."
|
94
|
+
return
|
95
|
+
end
|
96
|
+
@bot.config.plugins.options[const][option.to_sym] = eval(value)
|
97
|
+
|
98
|
+
m.reply "Successfuly set option."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/plugins/redmine.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'yajl'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Cinch
|
6
|
+
module Plugins
|
7
|
+
class Redmine
|
8
|
+
include Cinch::Plugin
|
9
|
+
|
10
|
+
timer 60, method: :check_redmine
|
11
|
+
|
12
|
+
set :plugin_name, 'redmine'
|
13
|
+
set :help, <<EOT
|
14
|
+
Redmine plugins checks the new issues in the specified redmine instance
|
15
|
+
EOT
|
16
|
+
|
17
|
+
def check_redmine
|
18
|
+
@issues ||= Array.new
|
19
|
+
api_key = @bot.config.options['cogconf']['redmine']['api_key']
|
20
|
+
redmine_url = "%s/issues.json?project_id=%s&limit=20" % [
|
21
|
+
@bot.config.options['cogconf']['redmine']['url'],
|
22
|
+
@bot.config.options['cogconf']['redmine']['project']
|
23
|
+
]
|
24
|
+
res = Yajl::Parser.parse(open(redmine_url, "X-Redmine-API-Key" => api_key))
|
25
|
+
subjs = Hash[ res['issues'].map { |i| [ i['id'], i['subject'] ] } ]
|
26
|
+
issues = subjs.keys
|
27
|
+
newones = Array.new
|
28
|
+
if @issues.count == 0
|
29
|
+
@issues = issues
|
30
|
+
else
|
31
|
+
issues.each { |i| newones << i unless @issues.include? i }
|
32
|
+
if newones.count > 0
|
33
|
+
newones.each do |i|
|
34
|
+
@issues.push i
|
35
|
+
@bot.config.options['cogconf']['main']['channels'].each do |channel|
|
36
|
+
Channel(channel).msg "[%s] >>> %s - %s/issues/%s" % [
|
37
|
+
@bot.config.options['cogconf']['redmine']['project'],
|
38
|
+
subjs[i],
|
39
|
+
@bot.config.options['cogconf']['redmine']['url'],
|
40
|
+
i
|
41
|
+
]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@issues = @issues[0..50]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
# puts "redmine: done #{newones.count} new issues, #{@issues.count} cached"
|
48
|
+
rescue Exception => e
|
49
|
+
puts "*** #{e.class}\n"
|
50
|
+
puts e.to_s
|
51
|
+
puts e.backtrace
|
52
|
+
end
|
53
|
+
|
54
|
+
def new(bot)
|
55
|
+
@bot = bot
|
56
|
+
@issues = Array.new
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/plugins/rubygems.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'yajl'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Cinch
|
6
|
+
module Plugins
|
7
|
+
class Rubygems
|
8
|
+
include Cinch::Plugin
|
9
|
+
|
10
|
+
match /r (.*)$/
|
11
|
+
|
12
|
+
set :plugin_name, 'rubygems'
|
13
|
+
set :help, <<EOT
|
14
|
+
Rubygems queries http://rubygems.org for finding gems
|
15
|
+
EOT
|
16
|
+
|
17
|
+
def query(query)
|
18
|
+
url = "https://rubygems.org/api/v1/search.json?query=#{CGI.escape(query)}"
|
19
|
+
back = "Requesting ... \n"
|
20
|
+
begin
|
21
|
+
file = open(url)
|
22
|
+
if file.class == StringIO
|
23
|
+
json = Yajl::Parser.parse(file)
|
24
|
+
if json.empty?
|
25
|
+
return "Nothing matches '#{query}'"
|
26
|
+
end
|
27
|
+
else
|
28
|
+
json = Yajl::Parser.parse(File.read(file))
|
29
|
+
end
|
30
|
+
4.times do |it|
|
31
|
+
back += "#{json[it]['name']} : #{json[it]['homepage_uri']}\n"
|
32
|
+
end
|
33
|
+
rescue Exception => e
|
34
|
+
p file.class
|
35
|
+
back = "*** #{e.class}\n"
|
36
|
+
back += e.to_s
|
37
|
+
end
|
38
|
+
return back
|
39
|
+
end
|
40
|
+
|
41
|
+
def new(bot)
|
42
|
+
@bot = bot
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute(m,words)
|
46
|
+
m.reply(query(words.strip))
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/plugins/shorturl.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module Cinch
|
4
|
+
module Plugins
|
5
|
+
class Shorturl
|
6
|
+
include Cinch::Plugin
|
7
|
+
|
8
|
+
match /(http:\/\/[^ ]*)/, :use_prefix => false
|
9
|
+
|
10
|
+
set :plugin_name, 'shorturl'
|
11
|
+
set :help, <<EOT
|
12
|
+
Shorturl catches urls said on channel and shorten then
|
13
|
+
EOT
|
14
|
+
|
15
|
+
def shorten(url)
|
16
|
+
url = open("http://tinyurl.com/api-create.php?url=#{URI.escape(url)}").read
|
17
|
+
url == "Error" ? nil : url
|
18
|
+
rescue OpenURI::HTTPError
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def new(bot)
|
23
|
+
@bot = bot
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute(m,url)
|
27
|
+
m.reply("Shortening: " + shorten(url))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class Stackoverflow
|
5
|
+
include Cinch::Plugin
|
6
|
+
|
7
|
+
match /s (.+)/
|
8
|
+
|
9
|
+
set :plugin_name, 'stackoverflow'
|
10
|
+
set :help, <<EOT
|
11
|
+
Stackoverflow returns the first match on a search
|
12
|
+
.s <keywords> : searches on those keywords
|
13
|
+
EOT
|
14
|
+
|
15
|
+
def new(bot)
|
16
|
+
@bot = bot
|
17
|
+
end
|
18
|
+
|
19
|
+
def search(query)
|
20
|
+
url = "http://api.stackoverflow.com/docs/search#site=stackoverflow&sort=activity&intitle=#{CGI.escape(query)}"
|
21
|
+
p url
|
22
|
+
res = JSON.parse(open(url))
|
23
|
+
p res
|
24
|
+
res.items.each do |i|
|
25
|
+
title = i.title
|
26
|
+
link = i.link
|
27
|
+
desc = i.tags.join(', ')
|
28
|
+
end
|
29
|
+
"#{title} - #{desc} (#{link})"
|
30
|
+
rescue
|
31
|
+
"No results found"
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute(m, query)
|
35
|
+
m.reply(search(query))
|
36
|
+
end
|
37
|
+
end
|
data/plugins/tests.rb
ADDED
data/plugins/tweet.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'twitter'
|
2
|
+
|
3
|
+
module Cinch
|
4
|
+
module Plugins
|
5
|
+
class Tweet
|
6
|
+
include Cinch::Plugin
|
7
|
+
|
8
|
+
match /t ?([^ ]*)?( ?.*)/
|
9
|
+
|
10
|
+
set :plugin_name, 'tweet'
|
11
|
+
set :help, <<EOT
|
12
|
+
Tweet makes the bot can query twittter API
|
13
|
+
.t search <term> : searches the public timelines
|
14
|
+
EOT
|
15
|
+
|
16
|
+
=begin
|
17
|
+
Twitter.configure do |c|
|
18
|
+
c.consumer_key = @bot.config.options.cogconf['tweet']['consumer_key']
|
19
|
+
c.consumer_secret = @bot.config.options.cogconf['tweet']['consumer_secret']
|
20
|
+
c.oauth_token = @bot.config.options.cogconf['tweet']['oauth_token']
|
21
|
+
c.oauth_token_secret = @bot.config.options.cogconf['tweet']['oauth_token_secret']
|
22
|
+
end
|
23
|
+
=end
|
24
|
+
|
25
|
+
def new(bot)
|
26
|
+
@bot = bot
|
27
|
+
end
|
28
|
+
|
29
|
+
def exec(command,args)
|
30
|
+
back = ''
|
31
|
+
#back += "command: #{command} / args: #{args}\n"
|
32
|
+
begin
|
33
|
+
case command
|
34
|
+
when 'search'
|
35
|
+
Twitter.search(args, { :lang => 'en', :rpp => '3' }).each do|status|
|
36
|
+
back += status.full_text + "\n"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
back += Twitter.send(command,args.split(','))
|
40
|
+
end
|
41
|
+
rescue Exception => e
|
42
|
+
back += "Bad request\n"
|
43
|
+
back += e.inspect
|
44
|
+
end
|
45
|
+
return back
|
46
|
+
end
|
47
|
+
|
48
|
+
def execute(m,command,args)
|
49
|
+
m.reply(exec(command,args.strip))
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/plugins/urban.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Cinch
|
6
|
+
module Plugins
|
7
|
+
class Urban
|
8
|
+
include Cinch::Plugin
|
9
|
+
|
10
|
+
match /u (.*)$/
|
11
|
+
|
12
|
+
set :plugin_name, 'urban'
|
13
|
+
set :help, <<EOT
|
14
|
+
Urban connects to urban dictionary and returns the first result for a given query, replying with the result directly to the sender
|
15
|
+
EOT
|
16
|
+
|
17
|
+
def query(query)
|
18
|
+
url = "http://www.urbandictionary.com/define.php?term=#{CGI.escape(query)}"
|
19
|
+
begin
|
20
|
+
CGI.unescape_html Nokogiri::HTML(open(url)).at("div.definition").text.gsub(/\s+/, ' ')
|
21
|
+
rescue
|
22
|
+
"no result found"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def new(bot)
|
27
|
+
@bot = bot
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute(m,words)
|
31
|
+
m.reply(query(words.strip))
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/plugins/users.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
module Cinch
|
3
|
+
module Plugins
|
4
|
+
class Users
|
5
|
+
|
6
|
+
class UserStruct < Struct.new(:user, :channel, :text, :time)
|
7
|
+
end
|
8
|
+
|
9
|
+
include Cinch::Plugin
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
storage[:users] ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
match /u (.+ )?([^\s]+)( .+)/
|
17
|
+
|
18
|
+
def exec(query,nick)
|
19
|
+
back ''
|
20
|
+
query.strip! rescue ''
|
21
|
+
nick.strip! rescue ''
|
22
|
+
if query.blank?
|
23
|
+
else
|
24
|
+
case query
|
25
|
+
when 'add'
|
26
|
+
else
|
27
|
+
back = "Unknown command #{query}."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return back
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute(m, query, nick, msg)
|
34
|
+
m.reply(exec(query, nick, msg))
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'minitest/ci'
|
2
|
+
require 'simplecov'
|
3
|
+
require 'simplecov-rcov'
|
4
|
+
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
6
|
+
SimpleCov.start
|
7
|
+
|
8
|
+
if $0 == __FILE__
|
9
|
+
TestHelpers::load_env
|
10
|
+
puts ENV.inspect
|
11
|
+
end
|
12
|
+
|
13
|
+
MiniTest::Ci.auto_clean = false
|