cogbot 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e507f5555aea13239fa0c558da83b0adaff0769c
4
- data.tar.gz: bcb41ab67f0dec2678ab884d5eff810907afbf94
3
+ metadata.gz: ed9500a2174f92bcc12ab3cae3e2654a3607267a
4
+ data.tar.gz: f45b95e057fb6c7610fe6093f9e473b7d138566f
5
5
  SHA512:
6
- metadata.gz: ad328acddaf1f81bed239d03e5c1f7d052a820ec1569ca2092ff0f6512b7201bba96eb35c5c27a8e207fff2e7ddae05bf9a3846961ac04dff8da5df1a1360d02
7
- data.tar.gz: a8604ada50f361692af7a5f097f2564e0f4f4cf552e2a018f1213acb63411aa6d4c852033e373626e1d21c9acd865e80412a90d5e172cbf825c379d496d5a1db
6
+ metadata.gz: 8ce87667fc15508303457c29a78dee2070166f1373128737c1ec98b34a8fdae4829db43befe5b22fc5a380853aefa07921ea999fc71d1279e7e2e04a2f75c915
7
+ data.tar.gz: 2e695e32ea44f41fa127fe5d24772b0dad73784779f928cc7a37ac35c1de9b7b7f9e3ca2d8be8ecbbc5b36a802bd04c312e980d01949f48b70139dc47532b909
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  config/cogbot.yml
4
4
  vendor/
5
+ *.swp
6
+ *.swo
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
- ## v0.1.1 - 2015-01-28
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
- ## v0.1.0 - 2015-01-08
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
- ## v0.0.3 - 2013-07-29
23
+ ### v0.0.3 - 2013-07-29
15
24
 
16
- ## v0.0.2 - 2013-04-03
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.1)
5
- cinch (~> 2.2.2)
4
+ cogbot (0.1.2)
5
+ cinch (= 2.2.4)
6
6
  daemons (~> 1.1.9)
7
- eventmachine (~> 1.0.4)
7
+ eventmachine (= 1.0.7)
8
8
  eventmachine_httpserver (~> 0.2.1)
9
9
  fortune_gem (~> 0.0.8)
10
- json (~> 1.8.1)
11
- nokogiri (~> 1.6.5)
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.6)
19
+ addressable (2.3.7)
20
20
  awesome_print (1.6.1)
21
21
  buftok (0.2.0)
22
- cinch (2.2.3)
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.4)
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.1)
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.4)
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
- [![Gem Version](https://badge.fury.io/rb/cogbot.svg)](http://badge.fury.io/rb/cogbot)
3
+ [![Gem Version](http://img.shields.io/gem/v/cogbot.svg)](http://rubygems.org/gems/cogbot)
4
+ [![Downloads](http://img.shields.io/gem/dt/cogbot.svg)](https://rubygems.org/gems/cogbot)
5
+ [![Dependency Status](https://img.shields.io/gemnasium/mose/cogbot.svg)](https://gemnasium.com/mose/cogbot)
6
+ [![Code Climate](http://img.shields.io/codeclimate/github/mose/cogbot.svg)](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', '~> 2.2.2'
19
+ gem.add_dependency 'cinch', '2.2.4'
20
20
  gem.add_dependency "thor", '~> 0.19.1'
21
- gem.add_dependency "eventmachine", '~> 1.0.4'
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.5'
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.1' # stackoverflow plugin
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
 
@@ -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.handlers.dispatch(:api_callback, nil, @http_post_content)
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)
@@ -1,3 +1,3 @@
1
1
  module Cogbot
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -7,7 +7,7 @@ module Cinch
7
7
 
8
8
  set :plugin_name, 'gitlistener'
9
9
 
10
- listen_to :api_callback
10
+ listen_to :http_gitlistener
11
11
 
12
12
  def listen(m, json)
13
13
  hash = Yajl::Parser.parse(URI.unescape(json[8..-1]))
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
- 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.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
- begin
31
- load(file_name)
32
- rescue Exception
33
- m.reply "Could not load #{plugin}."
34
- raise
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
- 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
39
+ begin
40
+ load(file_name)
41
+ rescue Exception
42
+ m.reply "Could not load #{plugin}."
43
+ raise
44
+ end
43
45
 
44
- @bot.plugins.register_plugin(const)
45
- m.reply "Successfully loaded #{plugin}"
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
- 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
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
- @bot.plugins.select {|p| p.class == plugin_class}.each do |p|
57
- @bot.plugins.unregister_plugin(p)
58
- end
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
- ## 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}"
92
+ m.reply "Successfully unloaded #{plugin}"
93
+ end
82
94
  end
83
95
 
84
96
  def reload_plugin(m, plugin)
85
- unload_plugin(m, plugin)
86
- load_plugin(m, plugin)
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
- 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
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}") + " " + status.text + "\n"
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.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-01-28 00:00:00.000000000 Z
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.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.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.4
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.4
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.5
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.5
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.1
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.1
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