ruboty-redmine 0.0.3 → 0.0.4

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: 80d38692ef9bfa4c14e4641be38f067786234bb8
4
- data.tar.gz: a6be7d580cf0b3ebcdf9780583e7f39181b176b1
3
+ metadata.gz: bcf104205740a7675b39dc610baaf02a1bd44dbf
4
+ data.tar.gz: e6904c6198c2673dc4c6e999ccb37c2e02f77527
5
5
  SHA512:
6
- metadata.gz: c45f767065539bd4f8457a93e6853a7c7fdef682915b35b5981e403e640c1de0aaada64ce838e4d517a6751ccd85168337132a251db9d58d9816b5a5f6e286ba
7
- data.tar.gz: 1d8f01c07cf5ba3317fc1345ee269fdfe1fb2eff811ebd46ab73050361ef05c91d480b6372b502b5fefa5bac73cad749e10b2a5646badde03216bba06370b100
6
+ metadata.gz: f28bc2644829ea3ad70ee3d8594a46ef779e08c5d75d16d0d4f82191b18f02d7e412937e6084a9fb2533abba48ccf3a6455fc7241bc9c5750ef90ba47a96f3a5
7
+ data.tar.gz: ed581a00f88e7932de5aa9f8ceb7d600b7519e06ac44af64685f2d7ae1494e7af04e840e7bd0b406fd48ebdd668edb279650910f7c86745f8b90cb295ec36d11
@@ -1,10 +1,13 @@
1
1
  module Ruboty
2
2
  module Handlers
3
3
  class Redmine < Base
4
+ NAMESPACE = 'redmine'
5
+
4
6
  env :REDMINE_URL, 'Redmine url (e.g. http://your-redmine)', optional: false
5
7
  env :REDMINE_API_KEY, 'Redmine REST API key', optional: false
6
8
  env :REDMINE_BASIC_AUTH_USER, 'Basic Auth User', optional: true
7
9
  env :REDMINE_BASIC_AUTH_PASSWORD, 'Basic Auth Password', optional: true
10
+ env :REDMINE_CHECK_INTERVAL, 'Interval to check new issues', optional: true
8
11
 
9
12
  on(
10
13
  /create issue (?<rest>.+)/,
@@ -12,10 +15,52 @@ module Ruboty
12
15
  description: 'Create a new issue'
13
16
  )
14
17
 
18
+ on(
19
+ /register redmine alias "(?<name>[^"]+)" ("(?<expand_to>[^"]+)"|'(?<expand_to>[^']+)')/,
20
+ name: 'register_alias',
21
+ description: 'Register an alias'
22
+ )
23
+
24
+ on(
25
+ /watch redmine issues in "(?<tracker>[^"]+)" tracker of "(?<project>[^"]+)" project( and assign to (?<assignees>[\d,]+)|)/,
26
+ name: 'watch_issues',
27
+ description: 'Watch issues'
28
+ )
29
+
30
+ on(
31
+ /list watching redmine issues/,
32
+ name: 'list_watching',
33
+ description: 'List watching issues'
34
+ )
35
+
36
+ on(
37
+ /stop watching redmine issues (?<id>\d+)/,
38
+ name: 'stop_watching',
39
+ description: 'Stop watching issues',
40
+ )
41
+
42
+ on(
43
+ /associate redmine user (?<redmine_id>\d+) with "(?<chat_name>[^"]+)"/,
44
+ name: 'associate_user',
45
+ description: 'Associate redmine_id with chat_name',
46
+ )
47
+
48
+ def initialize(*args)
49
+ super
50
+
51
+ start_to_watch_issues
52
+ end
53
+
15
54
  def create_issue(message)
16
- words = message[:rest].scan(/("([^"]+)"|([^ ]+))/).map {|v| v[1] || v[2] }
55
+ words = parse_arg(message[:rest])
17
56
  req = {}
18
57
  req[:subject] = words.shift
58
+
59
+ if words.size == 1
60
+ expand_to = alias_for(words.first)
61
+ words = parse_arg(expand_to) if expand_to
62
+ end
63
+
19
64
  words.each_with_index do |word, i|
20
65
  next if i == 0
21
66
 
@@ -23,13 +68,7 @@ module Ruboty
23
68
 
24
69
  case word
25
70
  when 'project'
26
- project = redmine.projects.find do |project|
27
- [
28
- project.id.to_s,
29
- project.name.downcase,
30
- project.identifier.downcase,
31
- ].include?(arg.downcase)
32
- end
71
+ project = redmine.find_project(arg)
33
72
 
34
73
  unless project
35
74
  message.reply("Project '#{arg}' is not found.")
@@ -38,12 +77,7 @@ module Ruboty
38
77
 
39
78
  req[:project] = project
40
79
  when 'tracker'
41
- tracker = redmine.trackers.find do |tracker|
42
- [
43
- tracker.id.to_s,
44
- tracker.name.downcase,
45
- ].include?(arg.downcase)
46
- end
80
+ tracker = redmine.find_tracker(arg)
47
81
 
48
82
  unless tracker
49
83
  message.reply("Tracker '#{arg}' is not found.")
@@ -63,16 +97,160 @@ module Ruboty
63
97
  message.reply("Issue created: #{redmine.url_for_issue(issue)}")
64
98
  end
65
99
 
100
+ def register_alias(message)
101
+ aliases[message[:name]] = message[:expand_to]
102
+ message.reply("Registered.")
103
+ end
104
+
105
+ def watch_issues(message)
106
+ if message.match_data.names.include?('assignees')
107
+ assignees = message[:assignees].split(',').map(&:to_i)
108
+ else
109
+ assignees = []
110
+ end
111
+
112
+ watches << message.original.except(:robot).merge(
113
+ {id: watches.size + 1, project: message[:project], tracker: message[:tracker], assignees: assignees, assignee_index: 0}
114
+ ).stringify_keys
115
+ message.reply("Watching.")
116
+ end
117
+
118
+ def list_watching(message)
119
+ reply = watches.map do |watch|
120
+ s = "##{watch['id']} #{watch['tracker']} tracker in #{watch['project']} project"
121
+ if assignees = watch['assignees']
122
+ s << " and assign to #{assignees}"
123
+ end
124
+ end.join("\n")
125
+
126
+ message.reply(reply)
127
+ end
128
+
129
+ def stop_watching(message)
130
+ id = message[:id].to_i
131
+ watches.reject! do |watch|
132
+ watch['id'] == id
133
+ end
134
+
135
+ message.reply("Stopped.")
136
+ end
137
+
138
+ def associate_user(message)
139
+ users << {"redmine_id" => message[:redmine_id].to_i, "chat_name" => message[:chat_name]}
140
+
141
+ message.reply("Registered.")
142
+ end
143
+
66
144
  private
67
145
 
68
146
  def redmine
69
- Ruboty::Redmine::Client.new(
147
+ @redmine ||= Ruboty::Redmine::Client.new(
70
148
  ENV['REDMINE_URL'],
71
149
  ENV['REDMINE_API_KEY'],
72
150
  basic_auth_user: ENV['REDMINE_BASIC_AUTH_USER'],
73
151
  basic_auth_password: ENV['REDMINE_BASIC_AUTH_PASSWORD'],
74
152
  )
75
153
  end
154
+
155
+ def alias_for(name)
156
+ aliases[name]
157
+ end
158
+
159
+ def aliases
160
+ robot.brain.data["#{NAMESPACE}_aliases"] ||= {}
161
+ end
162
+
163
+ def watches
164
+ robot.brain.data["#{NAMESPACE}_watches"] ||= []
165
+ end
166
+
167
+ def users
168
+ robot.brain.data["#{NAMESPACE}_users"] ||= []
169
+ end
170
+
171
+ def find_user_by_id(id)
172
+ users.find {|user| user['redmine_id'] == id }
173
+ end
174
+
175
+ def parse_arg(text)
176
+ text.scan(/("([^"]+)"|'([^']+)'|([^ ]+))/).map do |v|
177
+ v.shift
178
+ v.find {|itself| itself }
179
+ end
180
+ end
181
+
182
+ def start_to_watch_issues
183
+ thread = Thread.start do
184
+ last_issues_for_watch = {}
185
+
186
+ while true
187
+ sleep (ENV['REDMINE_CHECK_INTERVAL'] || 30).to_i
188
+ Ruboty::Redmine.log("Checking new issues...")
189
+ watches.each do |watch|
190
+ project = redmine.find_project(watch['project'])
191
+ tracker = redmine.find_tracker(watch['tracker'])
192
+
193
+ issues = redmine.issues(project: project, tracker: tracker, sort: 'id:desc')
194
+ if last_issues = last_issues_for_watch[watch]
195
+ new_issues = []
196
+ issues.each do |issue|
197
+ found = last_issues.find do |last_issue|
198
+ last_issue.id == issue.id
199
+ end
200
+
201
+ if found
202
+ break
203
+ else
204
+ new_issues << issue
205
+ end
206
+ end
207
+
208
+ new_issues.each do |new_issue|
209
+ require 'pry'
210
+ Pry.config.input = STDIN
211
+ Pry.config.output = STDOUT
212
+ binding.pry
213
+ assignees = watch['assignees']
214
+ assignee = nil
215
+ unless assignees.empty?
216
+ assignee = assignees[watch['assignee_index'] % assignees.size]
217
+ watch['assignee_index'] += 1
218
+
219
+ assignee = find_user_by_id(assignee)
220
+ end
221
+
222
+ if assignee
223
+ redmine.update_issue(new_issue, assigned_to_id: assignee['redmine_id'])
224
+ end
225
+
226
+ msg = <<-EOC
227
+ New Issue of #{tracker.name} in #{project.name} project
228
+ -> #{new_issue.subject}
229
+ EOC
230
+
231
+ if assignee
232
+ msg += <<-EOC
233
+ -> Assigned to @#{assignee['chat_name']}
234
+ EOC
235
+ end
236
+
237
+ msg += <<-EOC
238
+ -> #{redmine.url_for_issue(new_issue)}
239
+ EOC
240
+
241
+ Message.new(
242
+ watch.symbolize_keys.merge(robot: robot)
243
+ ).reply(msg)
244
+ end
245
+ end
246
+
247
+ last_issues_for_watch[watch] = issues
248
+ end
249
+ end
250
+ end
251
+
252
+ thread.abort_on_exception = true
253
+ end
76
254
  end
77
255
  end
78
256
  end
@@ -1,9 +1,16 @@
1
+ require "active_support/core_ext/hash/keys"
2
+
1
3
  require "ruboty/redmine/version"
2
4
  require "ruboty/redmine/client"
3
5
  require "ruboty/handlers/redmine"
4
6
 
5
7
  module Ruboty
6
8
  module Redmine
7
- # Your code goes here...
9
+ def self.log(msg)
10
+ Ruboty.logger.info "[redmine] #{msg}"
11
+
12
+ # FIX: thix dirty hack.
13
+ Ruboty.logger.instance_variable_get(:@logdev).dev.flush
14
+ end
8
15
  end
9
16
  end
@@ -25,6 +25,37 @@ module Ruboty
25
25
  end
26
26
  end
27
27
 
28
+ def find_project(query)
29
+ projects.find do |project|
30
+ [
31
+ project.id.to_s,
32
+ project.name.downcase,
33
+ project.identifier.downcase,
34
+ ].include?(query.downcase)
35
+ end
36
+ end
37
+
38
+ def find_tracker(query)
39
+ trackers.find do |tracker|
40
+ [
41
+ tracker.id.to_s,
42
+ tracker.name.downcase,
43
+ ].include?(query.downcase)
44
+ end
45
+ end
46
+
47
+ def issues(opts)
48
+ params = {}
49
+ params[:project_id] = opts[:project].id if opts[:project]
50
+ params[:tracker_id] = opts[:tracker].id if opts[:tracker]
51
+ params[:sort] = opts[:sort] if opts[:sort]
52
+
53
+ res = JSON.parse(get('/issues.json', params).body)
54
+ res['issues'].map do |tracker|
55
+ OpenStruct.new(tracker)
56
+ end
57
+ end
58
+
28
59
  def create_issue(opts)
29
60
  req = {
30
61
  issue: {
@@ -39,6 +70,15 @@ module Ruboty
39
70
  )
40
71
  end
41
72
 
73
+ def update_issue(issue, opts)
74
+ req = {issue: opts}
75
+
76
+ res = put("/issues/#{issue.id}.json", req)
77
+ unless res.status == 200
78
+ raise "Updating issue failed. (#{res.body})"
79
+ end
80
+ end
81
+
42
82
  def url_for_issue(issue)
43
83
  URI.join(@url, "/issues/#{issue.id}")
44
84
  end
@@ -78,6 +118,15 @@ module Ruboty
78
118
  req.headers['Content-Type'] = 'application/json'
79
119
  end
80
120
  end
121
+
122
+ def put(path, params = {})
123
+ conn.put do |req|
124
+ req.url path
125
+ req.body = params.to_json
126
+ req.headers['X-Redmine-API-Key'] = @api_key
127
+ req.headers['Content-Type'] = 'application/json'
128
+ end
129
+ end
81
130
  end
82
131
  end
83
132
  end
@@ -1,5 +1,5 @@
1
1
  module Ruboty
2
2
  module Redmine
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  end
5
5
  end
@@ -19,7 +19,9 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_dependency "ruboty"
21
21
  spec.add_dependency "faraday"
22
+ spec.add_dependency "activesupport"
22
23
 
24
+ spec.add_development_dependency "pry-byebug"
23
25
  spec.add_development_dependency "bundler", "~> 1.7"
24
26
  spec.add_development_dependency "rake", "~> 10.0"
25
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruboty-redmine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryota Arai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-30 00:00:00.000000000 Z
11
+ date: 2015-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruboty
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: bundler
43
71
  requirement: !ruby/object:Gem::Requirement