lita-updates 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +3 -0
- data/README.md +51 -0
- data/Rakefile +6 -0
- data/lib/lita-updates.rb +15 -0
- data/lib/lita/handlers/updates.rb +62 -0
- data/lib/lita/updates.rb +15 -0
- data/lib/lita/updates/commands/clear_schedule.rb +34 -0
- data/lib/lita/updates/commands/data.rb +21 -0
- data/lib/lita/updates/commands/get_schedule.rb +40 -0
- data/lib/lita/updates/commands/set_schedule.rb +41 -0
- data/lib/lita/updates/commands/standup.rb +21 -0
- data/lib/lita/updates/conversation.rb +77 -0
- data/lib/lita/updates/data.rb +18 -0
- data/lib/lita/updates/report.rb +54 -0
- data/lib/lita/updates/schedule.rb +67 -0
- data/lib/lita/updates/slack_report.rb +30 -0
- data/lita-updates.gemspec +23 -0
- data/locales/en.yml +4 -0
- data/spec/lita/handlers/updates_spec.rb +4 -0
- data/spec/spec_helper.rb +6 -0
- data/templates/.gitkeep +0 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4002e96a6a90ba0eafed7aa75e9233e4039f3e538c4dcd4dd5df07b4d07650b7
|
4
|
+
data.tar.gz: 2491be7eb80850c67c526c7f845e06e002353ff37ec35d747555591a14335c83
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 17fd7171e033c85d1ab6fd5193797e50b56300831cc0af4167ae6d9e5cc2efa1fa5810f2bcca447898421dc7929a35928ff4a66d7d204a947cb07e976252f780
|
7
|
+
data.tar.gz: cd26cbda7e97ea4e56d3db8c822e11ec043c269ad44e701bf775b7483df4b7867102691805ba7d3b12173b2eb310a720c7b9e59d2d3279fee6a7dd5b7914e7b3
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at pat@freelancing-gods.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# lita-updates
|
2
|
+
|
3
|
+
Provides a structured standup discussion to share amongst team members, along with scheduling at specific times/days for each user.
|
4
|
+
|
5
|
+
The original code for this library was constructed by [Josh Bassett](https://twitter.com/nullobject) and others at [The Conversation](https://theconversation.com/au), and then generously shared with myself to extend and open source.
|
6
|
+
|
7
|
+
It has been written with Slack in mind as the adapter, but extensions to make it more friendly with other platforms are very welcome.
|
8
|
+
|
9
|
+
The converation prompts:
|
10
|
+
|
11
|
+
![the conversation prompts](https://pat-public.s3.amazonaws.com/lita-updates-conversation.png)
|
12
|
+
|
13
|
+
And the shared results:
|
14
|
+
|
15
|
+
![the shared results](https://pat-public.s3.amazonaws.com/lita-updates-results.png)
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
This handler adds the following public commands via direct messages to your Lita bot:
|
20
|
+
|
21
|
+
* `standup` – begins a standup conversation with three questions (what have you been working on, what's next, what's getting in the way). Once these questions are answered, the result is shared in the target channel.
|
22
|
+
* `standup schedule` – see your personal schedule for standup conversations.
|
23
|
+
* `standup schedule TIME DAY DAY DAY` – set your personal schedule for standup conversations. e.g. `standup schedule 10:00 monday tuesday friday`. All days are accepted, and the time is 24 hours, and respects the user's time zone if set (otherwise, defaults to UTC).
|
24
|
+
* `standup schedule clear` – clear out your personal schedule (useful for when you're on holiday).
|
25
|
+
|
26
|
+
If you want to get stuck into the internals of this handler, there are two hidden commands that may be useful:
|
27
|
+
|
28
|
+
* `standup export` – returns the JSON of each person's schedules.
|
29
|
+
* `standup import JSON` – sets up everyone's schedules. Don't use this command unless you're absolutely sure of what you're doing.
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
Add lita-updates to your Lita instance's Gemfile:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
gem "lita-updates"
|
37
|
+
```
|
38
|
+
|
39
|
+
Then, in your Lita configuration, you'll want to set the channel where standup messages are shared:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
config.handlers.updates.target = "standup"
|
43
|
+
```
|
44
|
+
|
45
|
+
## Contributing
|
46
|
+
|
47
|
+
Bug reports and pull requests are welcome [on GitHub](https://github.com/limbr/lita-updates). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
48
|
+
|
49
|
+
## License
|
50
|
+
|
51
|
+
lita-updates is developed and maintained by [Pat Allan](https://freelancing-gods.com) for [Limbr](https://limbr.org), based on code by the team at [The Conversation](https://theconversation.com/au), and is released under the open [MIT Licence](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/lib/lita-updates.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "lita"
|
2
|
+
require "lita-timing"
|
3
|
+
|
4
|
+
Lita.load_locales Dir[File.expand_path(
|
5
|
+
File.join("..", "..", "locales", "*.yml"), __FILE__
|
6
|
+
)]
|
7
|
+
|
8
|
+
require "lita/handlers/updates"
|
9
|
+
|
10
|
+
Lita::Handlers::Standup.template_root File.expand_path(
|
11
|
+
File.join("..", "..", "templates"),
|
12
|
+
__FILE__
|
13
|
+
)
|
14
|
+
|
15
|
+
require "lita/updates"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Lita
|
2
|
+
module Handlers
|
3
|
+
class Standup < Handler
|
4
|
+
MINUTE = 60 # seconds
|
5
|
+
|
6
|
+
config :target
|
7
|
+
|
8
|
+
on :loaded, :set_up_schedule
|
9
|
+
on :unhandled_message, :continue_standup
|
10
|
+
|
11
|
+
route /^standup$/i, :command => true, :help => {
|
12
|
+
"standup" => "Provide a standup report on demand"
|
13
|
+
} do |response|
|
14
|
+
Lita::Updates::Commands::Standup.call robot, redis, response.user
|
15
|
+
end
|
16
|
+
|
17
|
+
route /^standup schedule$/i, :command => true,
|
18
|
+
:help => {"standup schedule" => "Display your current schedule"} do |response|
|
19
|
+
Lita::Updates::Commands::GetSchedule.call robot, redis, response.user
|
20
|
+
end
|
21
|
+
|
22
|
+
route /^standup schedule clear$/, :command => true,
|
23
|
+
:help => {"standup schedule clear" => "Clear your scheduled standup"} do |response|
|
24
|
+
Lita::Updates::Commands::ClearSchedule.call robot, redis, response.user
|
25
|
+
end
|
26
|
+
|
27
|
+
route /^standup schedule \d/i, :command => true,
|
28
|
+
:help => {"standup schedule" => "Specify hour and days. e.g. `standup schedule 16:00 monday tuesday friday`"} do |response|
|
29
|
+
Lita::Updates::Commands::SetSchedule.call robot, redis, response.user,
|
30
|
+
response.message.body.gsub('standup schedule', '').strip
|
31
|
+
end
|
32
|
+
|
33
|
+
route /^standup export$/i, :command => true do |response|
|
34
|
+
Lita::Updates::Commands::Data.new(redis, response).export
|
35
|
+
end
|
36
|
+
|
37
|
+
route /^standup import/i, :command => true do |response|
|
38
|
+
Lita::Updates::Commands::Data.new(redis, response).import
|
39
|
+
end
|
40
|
+
|
41
|
+
def continue_standup(payload)
|
42
|
+
message = payload[:message]
|
43
|
+
|
44
|
+
# Only respond to private messages or testing locally:
|
45
|
+
return unless message.source.private_message || Lita.config.robot.adapter == :shell
|
46
|
+
|
47
|
+
# Ensure messages are not from the bot:
|
48
|
+
return if message.user.mention_name == robot.mention_name
|
49
|
+
|
50
|
+
# Continue the standup discussion
|
51
|
+
Lita::Updates::Commands::Standup.call robot, redis, message.user,
|
52
|
+
message
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_up_schedule(payload)
|
56
|
+
every(MINUTE) { |timer| Lita::Updates::Schedule.call robot, redis }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Lita.register_handler(Lita::Handlers::Standup)
|
data/lib/lita/updates.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Lita::Updates
|
2
|
+
module Commands
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require "lita/updates/commands/data"
|
7
|
+
require "lita/updates/commands/clear_schedule"
|
8
|
+
require "lita/updates/commands/get_schedule"
|
9
|
+
require "lita/updates/commands/set_schedule"
|
10
|
+
require "lita/updates/commands/standup"
|
11
|
+
require "lita/updates/conversation"
|
12
|
+
require "lita/updates/data"
|
13
|
+
require "lita/updates/report"
|
14
|
+
require "lita/updates/schedule"
|
15
|
+
require "lita/updates/slack_report"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Lita::Updates::Commands::ClearSchedule
|
2
|
+
def self.call(robot, redis, user)
|
3
|
+
new(robot, redis, user).call
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(robot, redis, user)
|
7
|
+
@robot = robot
|
8
|
+
@redis = redis
|
9
|
+
@user = user
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
schedule.delete user.mention_name
|
14
|
+
|
15
|
+
redis.set "lita-updates:schedule", JSON.dump(schedule)
|
16
|
+
|
17
|
+
Lita::Updates::Commands::GetSchedule.call robot, redis, user
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :robot, :redis, :user
|
23
|
+
|
24
|
+
def schedule
|
25
|
+
@schedule ||= JSON.load schedule_raw
|
26
|
+
end
|
27
|
+
|
28
|
+
def schedule_raw
|
29
|
+
raw = redis.get("lita-updates:schedule")
|
30
|
+
return "{}" if raw.nil? || raw.empty?
|
31
|
+
|
32
|
+
raw
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Lita::Updates::Commands::Data
|
2
|
+
def initialize(redis, response)
|
3
|
+
@redis = redis
|
4
|
+
@response = response
|
5
|
+
end
|
6
|
+
|
7
|
+
def export
|
8
|
+
response.reply "JSON: \`#{redis.get("lita-updates:schedule")}\`"
|
9
|
+
end
|
10
|
+
|
11
|
+
def import
|
12
|
+
json = response.message.body.gsub(/^standup import\s+/, "")
|
13
|
+
redis.set "lita-updates:schedule", json
|
14
|
+
|
15
|
+
response.reply "The schedule data has been updated."
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :redis, :response
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Lita::Updates::Commands::GetSchedule
|
2
|
+
def self.call(robot, redis, user)
|
3
|
+
new(robot, redis, user).call
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(robot, redis, user)
|
7
|
+
@robot = robot
|
8
|
+
@redis = redis
|
9
|
+
@user = user
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
hash = schedule[user.mention_name]
|
14
|
+
|
15
|
+
if hash.nil?
|
16
|
+
say "No standup scheduled"
|
17
|
+
else
|
18
|
+
say "Standup scheduled for #{hash["time"]} on #{hash["days"].join(", ")}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :robot, :redis, :user
|
25
|
+
|
26
|
+
def schedule
|
27
|
+
@schedule ||= JSON.load schedule_raw
|
28
|
+
end
|
29
|
+
|
30
|
+
def schedule_raw
|
31
|
+
raw = redis.get("lita-updates:schedule")
|
32
|
+
return "{}" if raw.nil? || raw.empty?
|
33
|
+
|
34
|
+
raw
|
35
|
+
end
|
36
|
+
|
37
|
+
def say(message)
|
38
|
+
robot.send_message Lita::Source.new(:user => user), message
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Lita::Updates::Commands::SetSchedule
|
2
|
+
def self.call(robot, redis, user, message)
|
3
|
+
new(robot, redis, user, message).call
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(robot, redis, user, message)
|
7
|
+
@robot = robot
|
8
|
+
@redis = redis
|
9
|
+
@user = user
|
10
|
+
@message = message
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
data = message.split(/,?\s+/)
|
15
|
+
|
16
|
+
write data[0], data[1..-1].collect(&:downcase)
|
17
|
+
|
18
|
+
Lita::Updates::Commands::GetSchedule.call robot, redis, user
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :robot, :redis, :user, :message
|
24
|
+
|
25
|
+
def schedule
|
26
|
+
@schedule ||= JSON.load schedule_raw
|
27
|
+
end
|
28
|
+
|
29
|
+
def schedule_raw
|
30
|
+
raw = redis.get("lita-updates:schedule")
|
31
|
+
return "{}" if raw.nil? || raw.empty?
|
32
|
+
|
33
|
+
raw
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(time, days)
|
37
|
+
schedule[user.mention_name] = {"time" => time, "days" => days}
|
38
|
+
|
39
|
+
redis.set "lita-updates:schedule", JSON.dump(schedule)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Lita::Updates::Commands::Standup
|
2
|
+
def self.call(robot, redis, user, message = nil)
|
3
|
+
new(robot, redis, user, message).call
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(robot, redis, user, message)
|
7
|
+
@robot = robot
|
8
|
+
@redis = redis
|
9
|
+
@user = user
|
10
|
+
@message = message
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
puts "Starting standup with #{user.mention_name}" if message.nil?
|
15
|
+
Lita::Updates::Conversation.new(robot, redis, user, message).call
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :robot, :redis, :user, :message
|
21
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class Lita::Updates::Conversation
|
2
|
+
INTRODUCTION = <<-TXT
|
3
|
+
Hi there, it’s time for our standup meeting :smile:
|
4
|
+
There are just three questions (to skip a question, just reply with "None". To skip the standup, reply with "Cancel").
|
5
|
+
TXT
|
6
|
+
STATES = %w[ idle yesterday today blockers done ]
|
7
|
+
PROMPTS = {
|
8
|
+
"yesterday" => "#{INTRODUCTION}\nWhat have you been working on?",
|
9
|
+
"today" => "What are you working on next?",
|
10
|
+
"blockers" => "What blockers are impeding your progress?",
|
11
|
+
"done" => "Thanks! I’ll let everyone know what you’ve been up to."
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(robot, redis, user, message = nil)
|
15
|
+
@robot = robot
|
16
|
+
@data = Lita::Updates::Data.new redis, user
|
17
|
+
@user = user
|
18
|
+
@message = message
|
19
|
+
|
20
|
+
self.state = "idle" if state.nil? || state.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def call
|
24
|
+
return if message && state == "idle"
|
25
|
+
if message && message.body.downcase == "cancel"
|
26
|
+
say "This standup has been cancelled. To start again, just say `standup`."
|
27
|
+
self.state = "idle"
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
data.write state, message.body unless state == "idle" || message.nil?
|
32
|
+
|
33
|
+
self.state = next_state
|
34
|
+
say PROMPTS[state]
|
35
|
+
|
36
|
+
if state == "done"
|
37
|
+
report
|
38
|
+
self.state = "idle"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :robot, :data, :user, :message
|
45
|
+
|
46
|
+
def next_state
|
47
|
+
STATES[STATES.index(state) + 1] || "idle"
|
48
|
+
end
|
49
|
+
|
50
|
+
def state
|
51
|
+
data.read("state") || "idle"
|
52
|
+
end
|
53
|
+
|
54
|
+
def state=(value)
|
55
|
+
data.write "state", value
|
56
|
+
end
|
57
|
+
|
58
|
+
def say(text)
|
59
|
+
return if text.nil? || text.empty?
|
60
|
+
|
61
|
+
robot.send_message(Lita::Source.new(user: @user), text)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def report
|
66
|
+
reporter.new(robot, data, user).call
|
67
|
+
end
|
68
|
+
|
69
|
+
def reporter
|
70
|
+
case Lita.config.robot.adapter
|
71
|
+
when :slack
|
72
|
+
Lita::Updates::SlackReport
|
73
|
+
else
|
74
|
+
Lita::Updates::Report
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Lita::Updates::Data
|
2
|
+
def initialize(redis, user)
|
3
|
+
@redis = redis
|
4
|
+
@user = user
|
5
|
+
end
|
6
|
+
|
7
|
+
def read(key)
|
8
|
+
redis.get("#{user.mention_name}:#{key}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(key, value)
|
12
|
+
redis.set("#{user.mention_name}:#{key}", value)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :redis, :user
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Lita::Updates::Report
|
2
|
+
NOTHING = /^(none|nothing|nada)$/i
|
3
|
+
|
4
|
+
def initialize(robot, data, user)
|
5
|
+
@robot = robot
|
6
|
+
@data = data
|
7
|
+
@user = user
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
robot.send_message Lita::Source.new(user: user), text
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :robot, :data, :user
|
17
|
+
|
18
|
+
def answer(prompt, value)
|
19
|
+
return "" if value.strip.empty? || value[NOTHING]
|
20
|
+
|
21
|
+
"#{prompt}\n #{value}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def answers
|
25
|
+
[
|
26
|
+
answer("What have you just been working on?", yesterday),
|
27
|
+
answer("What's next?", today),
|
28
|
+
answer("What blockers are impeding your progress?", blockers)
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
def blockers
|
33
|
+
data.read "blockers"
|
34
|
+
end
|
35
|
+
|
36
|
+
def target
|
37
|
+
Lita::Room.find_by_name Lita.config.handlers.updates.target
|
38
|
+
end
|
39
|
+
|
40
|
+
def text
|
41
|
+
<<-TEXT.strip
|
42
|
+
#{user.name} posted a status update:
|
43
|
+
#{answers.join("\n\n")}
|
44
|
+
TEXT
|
45
|
+
end
|
46
|
+
|
47
|
+
def today
|
48
|
+
data.read "today"
|
49
|
+
end
|
50
|
+
|
51
|
+
def yesterday
|
52
|
+
data.read "yesterday"
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
class Lita::Updates::Schedule
|
4
|
+
def self.call(robot, redis)
|
5
|
+
new(robot, redis).call
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(robot, redis)
|
9
|
+
@robot = robot
|
10
|
+
@redis = redis
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
schedule.each do |username, hash|
|
15
|
+
user = Lita::User.fuzzy_find username
|
16
|
+
next unless user
|
17
|
+
|
18
|
+
Lita::Timing::Scheduled.new(
|
19
|
+
"standup-#{user.mention_name}", redis
|
20
|
+
).daily_at(
|
21
|
+
time_for(user, hash["time"]), hash["days"].collect(&:to_sym)
|
22
|
+
) do
|
23
|
+
puts "Starting standup with #{user.mention_name}"
|
24
|
+
Lita::Updates::Conversation.new(robot, redis, user).call
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :robot, :redis
|
32
|
+
|
33
|
+
def schedule
|
34
|
+
@schedule ||= JSON.load schedule_raw
|
35
|
+
end
|
36
|
+
|
37
|
+
def schedule_raw
|
38
|
+
raw = redis.get("lita-updates:schedule")
|
39
|
+
return "{}" if raw.nil? || raw.empty?
|
40
|
+
|
41
|
+
raw
|
42
|
+
end
|
43
|
+
|
44
|
+
def time_for(user, time)
|
45
|
+
user = extended_user_for user
|
46
|
+
now = Time.now
|
47
|
+
offset = user.metadata["tz_offset"] || now.utc_offset
|
48
|
+
hours, minutes = time.split(":").collect(&:to_i)
|
49
|
+
|
50
|
+
Time.new(now.year, now.month, now.day, hours, minutes, 0, offset).utc.
|
51
|
+
strftime("%k:%M").strip
|
52
|
+
end
|
53
|
+
|
54
|
+
def extended_user_for(user)
|
55
|
+
case Lita.config.robot.adapter
|
56
|
+
when :slack
|
57
|
+
Lita::Adapters::Slack::SlackUser.from_data robot.chat_service.api.send(
|
58
|
+
:call_api, "users.info", :user => user.id
|
59
|
+
)["user"]
|
60
|
+
else
|
61
|
+
user
|
62
|
+
end
|
63
|
+
rescue => error
|
64
|
+
puts "Error finding user #{username}"
|
65
|
+
user
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Lita::Updates::SlackReport < Lita::Updates::Report
|
2
|
+
def call
|
3
|
+
robot.chat_service.api.send(
|
4
|
+
:call_api,
|
5
|
+
"chat.postMessage",
|
6
|
+
as_user: false,
|
7
|
+
username: user.mention_name,
|
8
|
+
channel: target.id,
|
9
|
+
text: "#{user.name} posted a status update:",
|
10
|
+
attachments: JSON.dump(attachments.collect(&:to_hash))
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def answer(prompt, value, colour)
|
17
|
+
return nil if value.strip.empty? || value[NOTHING]
|
18
|
+
|
19
|
+
Lita::Adapters::Slack::Attachment.new value,
|
20
|
+
:title => prompt, :color => colour
|
21
|
+
end
|
22
|
+
|
23
|
+
def attachments
|
24
|
+
[
|
25
|
+
answer("What have you just been working on?", yesterday, "good"),
|
26
|
+
answer("What’s next?", today, "warning"),
|
27
|
+
answer("What blockers are impeding your progress?", blockers, "danger")
|
28
|
+
].compact
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "lita-updates"
|
3
|
+
spec.version = "0.1.0"
|
4
|
+
spec.authors = ["Pat Allan"]
|
5
|
+
spec.email = ["pat@freelancing-gods.com"]
|
6
|
+
spec.summary = "Standup Prompter for Slack"
|
7
|
+
spec.homepage = "https://github.com/limbrapp/lita-updates"
|
8
|
+
spec.license = "MIT"
|
9
|
+
spec.metadata = {"lita_plugin_type" => "handler"}
|
10
|
+
|
11
|
+
spec.files = `git ls-files`.split($/)
|
12
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
|
+
spec.require_paths = ["lib"]
|
15
|
+
|
16
|
+
spec.add_runtime_dependency "lita", ">= 4.7"
|
17
|
+
spec.add_runtime_dependency "lita-timing", "~> 0.3"
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
20
|
+
spec.add_development_dependency "rake"
|
21
|
+
spec.add_development_dependency "rack-test"
|
22
|
+
spec.add_development_dependency "rspec", ">= 3.0.0"
|
23
|
+
end
|
data/locales/en.yml
ADDED
data/spec/spec_helper.rb
ADDED
data/templates/.gitkeep
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lita-updates
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pat Allan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-05-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: lita
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: lita-timing
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
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'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.0.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.0.0
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- pat@freelancing-gods.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
106
|
+
- CODE_OF_CONDUCT.md
|
107
|
+
- Gemfile
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- lib/lita-updates.rb
|
111
|
+
- lib/lita/handlers/updates.rb
|
112
|
+
- lib/lita/updates.rb
|
113
|
+
- lib/lita/updates/commands/clear_schedule.rb
|
114
|
+
- lib/lita/updates/commands/data.rb
|
115
|
+
- lib/lita/updates/commands/get_schedule.rb
|
116
|
+
- lib/lita/updates/commands/set_schedule.rb
|
117
|
+
- lib/lita/updates/commands/standup.rb
|
118
|
+
- lib/lita/updates/conversation.rb
|
119
|
+
- lib/lita/updates/data.rb
|
120
|
+
- lib/lita/updates/report.rb
|
121
|
+
- lib/lita/updates/schedule.rb
|
122
|
+
- lib/lita/updates/slack_report.rb
|
123
|
+
- lita-updates.gemspec
|
124
|
+
- locales/en.yml
|
125
|
+
- spec/lita/handlers/updates_spec.rb
|
126
|
+
- spec/spec_helper.rb
|
127
|
+
- templates/.gitkeep
|
128
|
+
homepage: https://github.com/limbrapp/lita-updates
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata:
|
132
|
+
lita_plugin_type: handler
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 2.7.6
|
150
|
+
signing_key:
|
151
|
+
specification_version: 4
|
152
|
+
summary: Standup Prompter for Slack
|
153
|
+
test_files:
|
154
|
+
- spec/lita/handlers/updates_spec.rb
|
155
|
+
- spec/spec_helper.rb
|