cogbot 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +13 -4
- data/Gemfile.lock +10 -10
- data/README.md +41 -2
- data/cogbot.gemspec +4 -4
- data/config/cogbot.yml.defaults +11 -0
- data/lib/cogbot/server.rb +5 -1
- data/lib/cogbot/version.rb +1 -1
- data/plugins/gitlistener.rb +1 -1
- data/plugins/manager.rb +80 -63
- data/plugins/trellolistener.rb +211 -0
- data/plugins/tweet.rb +24 -1
- metadata +15 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed9500a2174f92bcc12ab3cae3e2654a3607267a
|
4
|
+
data.tar.gz: f45b95e057fb6c7610fe6093f9e473b7d138566f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ce87667fc15508303457c29a78dee2070166f1373128737c1ec98b34a8fdae4829db43befe5b22fc5a380853aefa07921ea999fc71d1279e7e2e04a2f75c915
|
7
|
+
data.tar.gz: 2e695e32ea44f41fa127fe5d24772b0dad73784779f928cc7a37ac35c1de9b7b7f9e3ca2d8be8ecbbc5b36a802bd04c312e980d01949f48b70139dc47532b909
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.2
|
1
|
+
2.2.1
|
data/CHANGELOG.md
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
Cogbot Changelog
|
2
2
|
===================
|
3
3
|
|
4
|
-
|
4
|
+
### v0.1.2 - 2015-03-16
|
5
|
+
- add link to status on twitter plugin search results
|
6
|
+
- add age of status in twitter plugin
|
7
|
+
- fix bug on tweet search results that are more than one day old or less than one minute
|
8
|
+
- add a weak protection for the manager plugin (which anyways should only be used in development mode)
|
9
|
+
- update dependencies on cinch and eventmachine (fixing a mem leak issue)
|
10
|
+
- add a trello listener plugin, for receiving hooks from Trello
|
11
|
+
- change the git listener to listen only on http://host:ip/gitlistener (to avoid confuse with the trello listener)
|
12
|
+
|
13
|
+
### v0.1.1 - 2015-01-28
|
5
14
|
- avoid disclose local path in nmanager plugin when plugin not found
|
6
15
|
- fix setup message config.yaml to cogbot.yaml
|
7
16
|
|
8
|
-
|
17
|
+
### v0.1.0 - 2015-01-08
|
9
18
|
- upgrade dependency to cinch 2.0.6 to 2.2.2
|
10
19
|
- upgrade other gems as well
|
11
20
|
- fix all plugins for upgrade
|
12
21
|
- handle compat with ruby 2.2
|
13
22
|
|
14
|
-
|
23
|
+
### v0.0.3 - 2013-07-29
|
15
24
|
|
16
|
-
|
25
|
+
### v0.0.2 - 2013-04-03
|
17
26
|
|
data/Gemfile.lock
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cogbot (0.1.
|
5
|
-
cinch (
|
4
|
+
cogbot (0.1.2)
|
5
|
+
cinch (= 2.2.4)
|
6
6
|
daemons (~> 1.1.9)
|
7
|
-
eventmachine (
|
7
|
+
eventmachine (= 1.0.7)
|
8
8
|
eventmachine_httpserver (~> 0.2.1)
|
9
9
|
fortune_gem (~> 0.0.8)
|
10
|
-
json (~> 1.8.
|
11
|
-
nokogiri (~> 1.6.
|
10
|
+
json (~> 1.8.2)
|
11
|
+
nokogiri (~> 1.6.6)
|
12
12
|
thor (~> 0.19.1)
|
13
13
|
twitter (~> 5.13.0)
|
14
14
|
yajl-ruby (~> 1.2.1)
|
@@ -16,19 +16,19 @@ PATH
|
|
16
16
|
GEM
|
17
17
|
remote: https://rubygems.org/
|
18
18
|
specs:
|
19
|
-
addressable (2.3.
|
19
|
+
addressable (2.3.7)
|
20
20
|
awesome_print (1.6.1)
|
21
21
|
buftok (0.2.0)
|
22
|
-
cinch (2.2.
|
22
|
+
cinch (2.2.4)
|
23
23
|
coderay (1.1.0)
|
24
24
|
daemons (1.1.9)
|
25
25
|
docile (1.1.5)
|
26
26
|
equalizer (0.0.9)
|
27
|
-
eventmachine (1.0.
|
27
|
+
eventmachine (1.0.7)
|
28
28
|
eventmachine_httpserver (0.2.1)
|
29
29
|
faraday (0.9.1)
|
30
30
|
multipart-post (>= 1.2, < 3)
|
31
|
-
flog (4.3.
|
31
|
+
flog (4.3.2)
|
32
32
|
ruby_parser (~> 3.1, > 3.1.0)
|
33
33
|
sexp_processor (~> 4.4)
|
34
34
|
fortune_gem (0.0.8)
|
@@ -66,7 +66,7 @@ GEM
|
|
66
66
|
simplecov (>= 0.4.1)
|
67
67
|
slop (3.6.0)
|
68
68
|
thor (0.19.1)
|
69
|
-
thread_safe (0.3.
|
69
|
+
thread_safe (0.3.5)
|
70
70
|
twitter (5.13.0)
|
71
71
|
addressable (~> 2.3)
|
72
72
|
buftok (~> 0.2.0)
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Cogbot
|
2
2
|
|
3
|
-
[](http://rubygems.org/gems/cogbot)
|
4
|
+
[](https://rubygems.org/gems/cogbot)
|
5
|
+
[](https://gemnasium.com/mose/cogbot)
|
6
|
+
[](https://codeclimate.com/github/mose/cogbot)
|
4
7
|
|
5
8
|
Cogbot is an irc bot written in ruby based on [Cinch bot framework](https://github.com/cinchrb/cinch).
|
6
9
|
|
@@ -10,10 +13,13 @@ team that uses irc as a main shared communication space:
|
|
10
13
|
|
11
14
|
* git notifications pushed on the channel
|
12
15
|
* redmine issues polled from redmine and announced
|
13
|
-
* commands to ask rubygems or stack overflow
|
16
|
+
* commands to ask google, rubygems or stack overflow
|
14
17
|
* the urban dictionary to make us laugh
|
18
|
+
* a twitter search plugin
|
19
|
+
* a trello webhooks listener
|
15
20
|
* and some other more or less used features
|
16
21
|
|
22
|
+
|
17
23
|
## Installation
|
18
24
|
|
19
25
|
gem install cogbot
|
@@ -27,6 +33,39 @@ At first launch:
|
|
27
33
|
you will be prompted to create a configuration file in ~/.cogbot/cogbot.yml
|
28
34
|
When this is done you can launch again and it will just run according to your configuration.
|
29
35
|
|
36
|
+
## Configuration
|
37
|
+
|
38
|
+
Some plugins require extra config parameters:
|
39
|
+
|
40
|
+
Git and trello webhook listeners use a small eventmachine http server, which is only launched if the configuration is present:
|
41
|
+
|
42
|
+
server:
|
43
|
+
ip: x.x.x.x
|
44
|
+
port: xxxxx
|
45
|
+
|
46
|
+
Twitter plugin requires to have credentials set:
|
47
|
+
|
48
|
+
tweet:
|
49
|
+
consumer_key: "xxx"
|
50
|
+
consumer_secret: "xxx"
|
51
|
+
access_token: "xxx"
|
52
|
+
access_token_secret: "xxx"
|
53
|
+
|
54
|
+
Trello plugin has some config too, for knowing where to announce the trello changes. The webhook has to be setup independantly, it's quite easy to declare by using postman.
|
55
|
+
|
56
|
+
trello:
|
57
|
+
announce:
|
58
|
+
- "#trello-announces"
|
59
|
+
|
60
|
+
Then in Trello, using the API, you can set a hook to send events to http://ip:port/trellolistener
|
61
|
+
|
62
|
+
## Todo
|
63
|
+
|
64
|
+
- document each plugin
|
65
|
+
- add multi-entrypoints system for webhooks listener
|
66
|
+
- add a users database
|
67
|
+
- add a credentials system
|
68
|
+
|
30
69
|
## Development
|
31
70
|
|
32
71
|
git clone git@github.com:mose/cogbot.git
|
data/cogbot.gemspec
CHANGED
@@ -16,15 +16,15 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.version = Cogbot::VERSION
|
18
18
|
|
19
|
-
gem.add_dependency 'cinch', '
|
19
|
+
gem.add_dependency 'cinch', '2.2.4'
|
20
20
|
gem.add_dependency "thor", '~> 0.19.1'
|
21
|
-
gem.add_dependency "eventmachine", '
|
21
|
+
gem.add_dependency "eventmachine", '1.0.7'
|
22
22
|
gem.add_dependency "eventmachine_httpserver", '~> 0.2.1'
|
23
|
-
gem.add_dependency 'nokogiri', '~> 1.6.
|
23
|
+
gem.add_dependency 'nokogiri', '~> 1.6.6'
|
24
24
|
gem.add_dependency "daemons", '~> 1.1.9'
|
25
25
|
|
26
26
|
gem.add_dependency 'twitter', '~> 5.13.0' # twitter plugin
|
27
|
-
gem.add_dependency 'json', '~> 1.8.
|
27
|
+
gem.add_dependency 'json', '~> 1.8.2' # stackoverflow plugin
|
28
28
|
gem.add_dependency 'yajl-ruby', '~> 1.2.1' # rubygems plugin
|
29
29
|
gem.add_dependency 'fortune_gem', '~> 0.0.8' # fortune plugin
|
30
30
|
|
data/config/cogbot.yml.defaults
CHANGED
@@ -12,6 +12,9 @@ main:
|
|
12
12
|
- 'dice'
|
13
13
|
- 'urban'
|
14
14
|
- 'tweet'
|
15
|
+
manager:
|
16
|
+
admin:
|
17
|
+
- 'mose'
|
15
18
|
server:
|
16
19
|
ip: 127.0.0.1
|
17
20
|
port: 9090
|
@@ -24,3 +27,11 @@ redmine:
|
|
24
27
|
api_key: ''
|
25
28
|
url: ''
|
26
29
|
project: ''
|
30
|
+
trello:
|
31
|
+
announce:
|
32
|
+
-
|
33
|
+
channel: "#trello"
|
34
|
+
board: "General"
|
35
|
+
-
|
36
|
+
channel: "#trello-dev"
|
37
|
+
board: "Dev"
|
data/lib/cogbot/server.rb
CHANGED
@@ -12,7 +12,11 @@ class Server < EM::Connection
|
|
12
12
|
|
13
13
|
def process_http_request
|
14
14
|
if @http_request_method == "POST"
|
15
|
-
@bot.
|
15
|
+
pluginlist = @bot.plugins.map { |e| e.class.name.split('::').last.downcase }
|
16
|
+
query = @http_request_uri[1..-1]
|
17
|
+
if pluginlist.include? query
|
18
|
+
@bot.handlers.dispatch("http_#{query}".to_sym, nil, @http_post_content)
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
response = EM::DelegatedHttpResponse.new(self)
|
data/lib/cogbot/version.rb
CHANGED
data/plugins/gitlistener.rb
CHANGED
data/plugins/manager.rb
CHANGED
@@ -10,6 +10,14 @@ module Cinch
|
|
10
10
|
match(/m reload (\S+)/, method: :reload_plugin)
|
11
11
|
match(/m set (\S+) (\S+) (.+)$/, method: :set_option)
|
12
12
|
|
13
|
+
def authorized(m, &block)
|
14
|
+
if @bot.config.options['cogconf']['manager']['admin'].include? m.user.nick
|
15
|
+
instance_eval(&block)
|
16
|
+
else
|
17
|
+
m.reply "Sorry, you don't have the right."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
def list_plugins(m)
|
14
22
|
back = ''
|
15
23
|
@bot.plugins.each { |p| back += "#{p.class.name.split('::').last.downcase} " }
|
@@ -17,85 +25,94 @@ module Cinch
|
|
17
25
|
end
|
18
26
|
|
19
27
|
def load_plugin(m, mapping)
|
20
|
-
|
21
|
-
|
22
|
-
|
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.basename(file_name)} does not exist."
|
27
|
-
return
|
28
|
-
end
|
28
|
+
authorized m do
|
29
|
+
plugin = mapping.downcase.camelize
|
30
|
+
p plugin
|
31
|
+
p mapping
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
33
|
+
file_name = "#{ROOT_DIR}/plugins/#{mapping.downcase}.rb"
|
34
|
+
unless File.exist?(file_name)
|
35
|
+
m.reply "Could not load #{plugin} because #{File.basename(file_name)} does not exist."
|
36
|
+
return
|
37
|
+
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
begin
|
40
|
+
load(file_name)
|
41
|
+
rescue Exception
|
42
|
+
m.reply "Could not load #{plugin}."
|
43
|
+
raise
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
+
begin
|
47
|
+
const = Cinch::Plugins.const_get(plugin)
|
48
|
+
rescue NameError
|
49
|
+
m.reply "Could not load #{plugin} because no matching class was found."
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
@bot.plugins.register_plugin(const)
|
54
|
+
m.reply "Successfully loaded #{plugin}"
|
55
|
+
end
|
46
56
|
end
|
47
57
|
|
48
58
|
def unload_plugin(m, plugin)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
59
|
+
authorized m do
|
60
|
+
begin
|
61
|
+
plugin_class = Cinch::Plugins.const_get(plugin.downcase.camelize)
|
62
|
+
rescue NameError
|
63
|
+
m.reply "Could not unload #{plugin} because no matching class was found."
|
64
|
+
return
|
65
|
+
end
|
55
66
|
|
56
|
-
|
57
|
-
|
58
|
-
|
67
|
+
@bot.plugins.select {|p| p.class == plugin_class}.each do |p|
|
68
|
+
@bot.plugins.unregister_plugin(p)
|
69
|
+
end
|
70
|
+
|
71
|
+
## FIXME not doing this at the moment because it'll break
|
72
|
+
## plugin options. This means, however, that reloading a
|
73
|
+
## plugin is relatively dirty: old methods will not be removed
|
74
|
+
## but only overwritten by new ones. You will also not be able
|
75
|
+
## to change a classes superclass this way.
|
76
|
+
# Cinch::Plugins.__send__(:remove_const, plugin)
|
77
|
+
|
78
|
+
# Because we're not completely removing the plugin class,
|
79
|
+
# reset everything to the starting values.
|
80
|
+
plugin_class.hooks.clear
|
81
|
+
plugin_class.matchers.clear
|
82
|
+
plugin_class.listeners.clear
|
83
|
+
plugin_class.timers.clear
|
84
|
+
plugin_class.ctcps.clear
|
85
|
+
plugin_class.react_on = :message
|
86
|
+
plugin_class.plugin_name = nil
|
87
|
+
plugin_class.help = nil
|
88
|
+
plugin_class.prefix = nil
|
89
|
+
plugin_class.suffix = nil
|
90
|
+
plugin_class.required_options.clear
|
59
91
|
|
60
|
-
|
61
|
-
|
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}"
|
92
|
+
m.reply "Successfully unloaded #{plugin}"
|
93
|
+
end
|
82
94
|
end
|
83
95
|
|
84
96
|
def reload_plugin(m, plugin)
|
85
|
-
|
86
|
-
|
97
|
+
authorized m do
|
98
|
+
unload_plugin(m, plugin)
|
99
|
+
load_plugin(m, plugin)
|
100
|
+
end
|
87
101
|
end
|
88
102
|
|
89
103
|
def set_option(m, plugin, option, value)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
104
|
+
authorized m do
|
105
|
+
begin
|
106
|
+
const = Cinch::Plugins.const_get(plugin.downcase.camelize)
|
107
|
+
rescue NameError
|
108
|
+
m.reply "Could not set plugin option for #{plugin} because no matching class was found."
|
109
|
+
return
|
110
|
+
end
|
111
|
+
@bot.config.plugins.options[const][option.to_sym] = eval(value)
|
112
|
+
|
113
|
+
m.reply "Successfuly set option."
|
95
114
|
end
|
96
|
-
@bot.config.plugins.options[const][option.to_sym] = eval(value)
|
97
115
|
|
98
|
-
m.reply "Successfuly set option."
|
99
116
|
end
|
100
117
|
end
|
101
118
|
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require "cgi"
|
2
|
+
|
3
|
+
module Cinch
|
4
|
+
module Plugins
|
5
|
+
class Trellolistener
|
6
|
+
include Cinch::Plugin
|
7
|
+
|
8
|
+
set :plugin_name, 'trellolistener'
|
9
|
+
|
10
|
+
listen_to :http_trellolistener
|
11
|
+
|
12
|
+
def listen(m, json)
|
13
|
+
hash = Yajl::Parser.parse(URI.unescape(json))
|
14
|
+
#bot.loggers.debug(hash.inspect)
|
15
|
+
if @bot.config.options['cogconf']['trello']
|
16
|
+
@bot.config.options['cogconf']['trello']['announce'].each do |announce|
|
17
|
+
if hash['action']['data']['board']['name'] == announce['board']
|
18
|
+
channel = announce['channel']
|
19
|
+
action = hash['action']['type']
|
20
|
+
case action
|
21
|
+
when 'updateBoard'
|
22
|
+
# not implemented yet
|
23
|
+
# "prefs":{"cardAging":"pirate"}
|
24
|
+
# "prefs":{"voting":"members"}
|
25
|
+
when 'addMemberToBoard'
|
26
|
+
message(channel, hash, "added %s to board" % [
|
27
|
+
Format(:aqua, hash['action']['member']['username'])
|
28
|
+
])
|
29
|
+
when 'removeMemberFromBoard'
|
30
|
+
message(channel, hash, "removed %s from board" % [
|
31
|
+
Format(:aqua, hash['action']['member']['username'])
|
32
|
+
])
|
33
|
+
when 'updateList'
|
34
|
+
message(channel, hash, "changed column name %s to %s" % [
|
35
|
+
Format(:orange, hash['action']['data']['old']['name']),
|
36
|
+
Format(:orange, hash['action']['data']['list']['name'])
|
37
|
+
])
|
38
|
+
when 'createCard'
|
39
|
+
message(channel, hash, "created \"%s\" in %s" % [
|
40
|
+
truncate(hash['action']['data']['card']['name']),
|
41
|
+
Format(:orange, hash['action']['data']['list']['name'])
|
42
|
+
])
|
43
|
+
when 'addMemberToCard'
|
44
|
+
message(channel, hash, "added %s to \"%s\"" % [
|
45
|
+
Format(:aqua, hash['action']['member']['username']),
|
46
|
+
truncate(hash['action']['data']['card']['name'])
|
47
|
+
]) or puts json
|
48
|
+
when 'removeMemberFromCard'
|
49
|
+
message(channel, hash, "removed %s from \"%s\"" % [
|
50
|
+
Format(:aqua, hash['action']['member']['username']),
|
51
|
+
truncate(hash['action']['data']['card']['name'])
|
52
|
+
]) or puts json
|
53
|
+
when 'updateCard'
|
54
|
+
if hash['action']['data']['old']
|
55
|
+
if hash['action']['data']['listAfter']
|
56
|
+
message(channel, hash, "moved \"%s\" from %s to %s" % [
|
57
|
+
truncate(hash['action']['data']['card']['name']),
|
58
|
+
Format(:orange, hash['action']['data']['listBefore']['name']),
|
59
|
+
Format(:orange, hash['action']['data']['listAfter']['name'])
|
60
|
+
])
|
61
|
+
elsif hash['action']['data']['old']['desc']
|
62
|
+
message(channel, hash, "changed desc on \"%s\" in %s to \"%s\"" % [
|
63
|
+
truncate(hash['action']['data']['card']['name']),
|
64
|
+
Format(:orange, hash['action']['data']['list']['name']),
|
65
|
+
truncate(hash['action']['data']['card']['desc'])
|
66
|
+
])
|
67
|
+
elsif hash['action']['data']['old']['closed'] != nil
|
68
|
+
if hash['action']['data']['card']['closed']
|
69
|
+
message(channel, hash, "archived \"%s\" from %s" % [
|
70
|
+
truncate(hash['action']['data']['card']['name']),
|
71
|
+
Format(:orange, hash['action']['data']['list']['name'])
|
72
|
+
])
|
73
|
+
else
|
74
|
+
message(channel, hash, "restored \"%s\" in %s" % [
|
75
|
+
truncate(hash['action']['data']['card']['name']),
|
76
|
+
Format(:orange, hash['action']['data']['list']['name'])
|
77
|
+
])
|
78
|
+
end
|
79
|
+
elsif hash['action']['data']['old'].has_key? 'due'
|
80
|
+
if hash['action']['data']['card']['due'] && hash['action']['data']['old']['due']
|
81
|
+
date_new = Date.parse(hash['action']['data']['card']['due']).strftime("%a %-d %b")
|
82
|
+
date_old = Date.parse(hash['action']['data']['old']['due']).strftime("%a %-d %b")
|
83
|
+
message(channel, hash, "changed date on \"%s\" in %s, from %s to %s" % [
|
84
|
+
truncate(hash['action']['data']['card']['name']),
|
85
|
+
Format(:orange, hash['action']['data']['list']['name']),
|
86
|
+
Format(:yellow, date_old),
|
87
|
+
Format(:yellow, date_new)
|
88
|
+
])
|
89
|
+
elsif hash['action']['data']['card']['due']
|
90
|
+
date_new = Date.parse(hash['action']['data']['card']['due']).strftime("%a %-d %b")
|
91
|
+
message(channel, hash, "set date on \"%s\" in %s, to %s" % [
|
92
|
+
truncate(hash['action']['data']['card']['name']),
|
93
|
+
Format(:orange, hash['action']['data']['list']['name']),
|
94
|
+
Format(:yellow, date_new)
|
95
|
+
])
|
96
|
+
else
|
97
|
+
date_old = Date.parse(hash['action']['data']['old']['due']).strftime("%a %-d %b")
|
98
|
+
message(channel, hash, "removed date on \"%s\" in %s, was \"%s\"" % [
|
99
|
+
truncate(hash['action']['data']['card']['name']),
|
100
|
+
Format(:orange, hash['action']['data']['list']['name']),
|
101
|
+
Format(:yellow, date_old)
|
102
|
+
])
|
103
|
+
end
|
104
|
+
else
|
105
|
+
puts "---- no known old ----"
|
106
|
+
bot.loggers.debug(json)
|
107
|
+
puts "---- / no known old ----"
|
108
|
+
end
|
109
|
+
else
|
110
|
+
puts "---- no old ----"
|
111
|
+
bot.loggers.debug(json)
|
112
|
+
puts "---- / no old ----"
|
113
|
+
end
|
114
|
+
when 'voteOnCard'
|
115
|
+
message(channel, hash, "voted on card \"%s\"" % [
|
116
|
+
truncate(hash['action']['data']['card']['name'])
|
117
|
+
])
|
118
|
+
when 'addLabelToCard'
|
119
|
+
message(channel, hash, "labelled \"%s\" as %s" % [
|
120
|
+
truncate(hash['action']['data']['card']['name']),
|
121
|
+
Format(:green, hash['action']['data']['label']['name'])
|
122
|
+
])
|
123
|
+
when 'removeLabelFromCard'
|
124
|
+
message(channel, hash, "unlabelled \"%s\" as %s" % [
|
125
|
+
truncate(hash['action']['data']['card']['name']),
|
126
|
+
Format(:grey, hash['action']['data']['label']['name'])
|
127
|
+
])
|
128
|
+
when 'commentCard'
|
129
|
+
message(channel, hash, "commented on \"%s\" in %s: %s" % [
|
130
|
+
truncate(hash['action']['data']['card']['name']),
|
131
|
+
Format(:orange, hash['action']['data']['list']['name']),
|
132
|
+
truncate(hash['action']['data']['text'])
|
133
|
+
])
|
134
|
+
when 'addChecklistToCard'
|
135
|
+
message(channel, hash, "added checklist \"%s\" on \"%s\"" % [
|
136
|
+
truncate(hash['action']['data']['checklist']['name']),
|
137
|
+
truncate(hash['action']['data']['card']['name'])
|
138
|
+
])
|
139
|
+
when 'updateChecklist'
|
140
|
+
message(channel, hash, "changed checklist \"%s\" to \"%s\"" % [
|
141
|
+
truncate(hash['action']['data']['checkItem']['name']),
|
142
|
+
truncate(hash['action']['data']['old']['name'])
|
143
|
+
])
|
144
|
+
when 'createCheckItem'
|
145
|
+
message(channel, hash, "added \"%s\" in checklist \"%s\" on \"%s\"" % [
|
146
|
+
truncate(hash['action']['data']['checkItem']['name']),
|
147
|
+
truncate(hash['action']['data']['checklist']['name']),
|
148
|
+
truncate(hash['action']['data']['card']['name'])
|
149
|
+
])
|
150
|
+
when 'updateCheckItem'
|
151
|
+
message(channel, hash, "changed \"%s\" in checklist \"%s\" to \"%s\" on \"%s\"" % [
|
152
|
+
truncate(hash['action']['data']['old']['name']),
|
153
|
+
truncate(hash['action']['data']['checklist']['name']),
|
154
|
+
truncate(hash['action']['data']['checkItem']['name']),
|
155
|
+
truncate(hash['action']['data']['card']['name'])
|
156
|
+
])
|
157
|
+
when 'updateCheckItemStateOnCard'
|
158
|
+
message(channel, hash, "changed state of \"%s\" in \"%s\" to %s on \"%s\"" % [
|
159
|
+
truncate(hash['action']['data']['checkItem']['name']),
|
160
|
+
truncate(hash['action']['data']['checklist']['name']),
|
161
|
+
Format(:yellow, hash['action']['data']['checkItem']['state']),
|
162
|
+
truncate(hash['action']['data']['card']['name'])
|
163
|
+
])
|
164
|
+
when 'moveCardFromBoard'
|
165
|
+
message(channel, hash, "moved card \"%s\" to board %s" % [
|
166
|
+
truncate(hash['action']['data']['card']['name']),
|
167
|
+
Format(:yellow, "[%s]" % hash['action']['data']['boardTarget']['name'])
|
168
|
+
])
|
169
|
+
else
|
170
|
+
puts "------------- not yet implemented: #{action} ------"
|
171
|
+
bot.loggers.debug(json)
|
172
|
+
puts "---------------------------------------------------"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def link(x)
|
182
|
+
"https://trello.com/c/#{x}"
|
183
|
+
end
|
184
|
+
|
185
|
+
def truncate(content, limit=50)
|
186
|
+
message = ''
|
187
|
+
words = content.gsub(/[\s\n]+/," ").split(" ")
|
188
|
+
while words.count > 0
|
189
|
+
word = words.shift
|
190
|
+
if (message.size + word.size + 2) > limit
|
191
|
+
message << ' …'
|
192
|
+
words.clear
|
193
|
+
else
|
194
|
+
message << " #{word}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
message.strip
|
198
|
+
end
|
199
|
+
|
200
|
+
def message(channel, hash, msg)
|
201
|
+
Channel(channel).send("%s %s %s %s" % [
|
202
|
+
Format(:yellow, "[%s]" % hash['action']['data']['board']['name']),
|
203
|
+
Format(:aqua, hash['action']['memberCreator']['username']),
|
204
|
+
msg,
|
205
|
+
Format(:grey, "(%s)" % link(hash['action']['data']['card']['shortLink']))
|
206
|
+
])
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
data/plugins/tweet.rb
CHANGED
@@ -26,6 +26,25 @@ EOT
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def ago(timestamp)
|
30
|
+
now = Time.now.utc
|
31
|
+
timespent = now - timestamp
|
32
|
+
case timespent
|
33
|
+
when 0..60
|
34
|
+
"#{timespent.round}s ago"
|
35
|
+
when 61..3600
|
36
|
+
"#{(timespent/60).floor}m ago"
|
37
|
+
when 3601..86400
|
38
|
+
"#{(timespent/3600).floor}h ago"
|
39
|
+
when 86401..2592000
|
40
|
+
"#{(timespent/86400).floor}d ago"
|
41
|
+
when 2592001..31536000
|
42
|
+
"#{(timespent/2592000).floor} month ago"
|
43
|
+
else
|
44
|
+
"more than one year ago"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
29
48
|
def new(bot)
|
30
49
|
@bot = bot
|
31
50
|
end
|
@@ -37,7 +56,11 @@ EOT
|
|
37
56
|
case command
|
38
57
|
when 'search'
|
39
58
|
client.search(args).take(3).each do |status|
|
40
|
-
back += Format(:bold, :underline, :yellow, "@#{status.user.screen_name}")
|
59
|
+
back += Format(:bold, :underline, :yellow, "@#{status.user.screen_name}")
|
60
|
+
back += " #{status.text.gsub(/\n/,' ')}"
|
61
|
+
back += " (#{ago(status.created_at)}"
|
62
|
+
back += " https://twitter.com/#{status.user.screen_name}/status/#{status.id})"
|
63
|
+
back += "\n"
|
41
64
|
end
|
42
65
|
else
|
43
66
|
back += "Usage: .t search <term> : searches the public timelines"
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cogbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mose
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cinch
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.2.
|
19
|
+
version: 2.2.4
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.2.
|
26
|
+
version: 2.2.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: thor
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: eventmachine
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.0.
|
47
|
+
version: 1.0.7
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.0.
|
54
|
+
version: 1.0.7
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: eventmachine_httpserver
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.6.
|
75
|
+
version: 1.6.6
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.6.
|
82
|
+
version: 1.6.6
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: daemons
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 1.8.
|
117
|
+
version: 1.8.2
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 1.8.
|
124
|
+
version: 1.8.2
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: yajl-ruby
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -297,6 +297,7 @@ files:
|
|
297
297
|
- plugins/shorturl.rb
|
298
298
|
- plugins/stackoverflow.rb
|
299
299
|
- plugins/tests.rb
|
300
|
+
- plugins/trellolistener.rb
|
300
301
|
- plugins/tweet.rb
|
301
302
|
- plugins/urban.rb
|
302
303
|
- plugins/users.rb
|