lita-reminder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d7da071e31a14f20aa78c44c315c59537b9f430f
4
+ data.tar.gz: aa8445b81afe0b95642eb95f4049b03ae82abb33
5
+ SHA512:
6
+ metadata.gz: 0c758a6243955fd23f7514bd886aa2c4d008dd03996f6b2fb5f0a3edc438c6b4300b7a0d12d8b183e493c4db0e5c6d4f37b147ddcfb6a7bdd8cb56c869b27b53
7
+ data.tar.gz: d55d6689615046e270bd3d4688bbf9c4152fff97c54105e3cc2603e5e10692b24dea5e77c5d761a720694dd77d1576be2927c0872b5b257a47efc169e3515929
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script: bundle exec rake
5
+ before_install:
6
+ - gem update --system
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 glebtv
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # lita-reminder
2
+
3
+ # Work in progress - not yet usable
4
+
5
+ This is an effort to add simple task scheduler to lita bot.
6
+ It is currenly in early development and not yet usable \ working
7
+
8
+ ## Installation
9
+
10
+ Add lita-reminder to your Lita instance's Gemfile:
11
+
12
+ ``` ruby
13
+ gem "lita-reminder"
14
+ ```
15
+
16
+ ## Configuration
17
+
18
+ TODO: Describe any configuration attributes the plugin exposes.
19
+
20
+ ## Usage
21
+
22
+ TODO: Describe the plugin's features and how to use them.
23
+
24
+ ## License
25
+
26
+ [MIT](http://opensource.org/licenses/MIT)
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,111 @@
1
+ require 'lita'
2
+ require 'rufus/scheduler'
3
+ require 'reminder/task'
4
+ require 'reminder/runner'
5
+ require 'reminder/domain'
6
+ require 'chronic'
7
+ require 'thread'
8
+
9
+ module Lita
10
+ module Handlers
11
+ class Reminder < Handler
12
+ @@mutex = Mutex.new
13
+ @@runner = ReminderRunner.new
14
+
15
+ route(/^remind\s+(?<who>\S+)\s+(?<type>at|in|every|cron)\s+(?<time>.*)(\s+first\s+at\s+(?<first>.*))?\s+to\s+(?<task>.*?)(\s+repeat\s+(?<repeat>.*)\s+times\s+(?<repeat_interval>.*))?$/, :add,
16
+ help: {"remind (me|here|username|room) (at|in|every|cron) TIME [first at TIME] to TASK [repeat 3|many times 10m]" => "Add a reminder"})
17
+ route(/^reminder\s+done\s+(\d+)$/, :done, help: {"reminder ID done" => "Stop nagging"})
18
+ route(/^reminder\s+delete\s+(\d+)$/, :delete, help: {"reminder ID delete" => "Delete reminder"})
19
+ route(/^reminder\s+list$/, :list, help: {"reminder list" => "List reminders"})
20
+ route(/^reminder\s+clear\s+all$/, :clear)
21
+
22
+ # reminders for domain name expiration
23
+ route(/^reminder\s+add\s+domain\s+(.*)$/, :add_domains, help: {"reminder add domain DOMAINS ..." => "Add domain expiration reminder"})
24
+ route(/^reminder\s+delete\s+domain\s+(.*)$/, :delete_domains, help: {"reminder delete domain DOMAINS ..." => "Stop watching domain"})
25
+ route(/^reminder\s+list\s+domains\s+(.*)$/, :list_domains, help: {"reminder list domains" => "List domain watch list"})
26
+
27
+ route(/^server\s+time$/, :time)
28
+
29
+ attr_accessor :scheduler
30
+
31
+ def initialize(robot)
32
+ super(robot)
33
+ @@mutex.synchronize do
34
+ @@runner.start(robot, redis)
35
+ end
36
+ end
37
+
38
+ def add(response)
39
+ @@mutex.synchronize do
40
+ @@runner.add(response)
41
+ end
42
+ end
43
+ def done(response)
44
+ @@mutex.synchronize do
45
+ @@runner.done(response)
46
+ end
47
+ end
48
+ def delete(response)
49
+ @@mutex.synchronize do
50
+ @@runner.delete(response)
51
+ end
52
+ end
53
+ def list(response)
54
+ @@mutex.synchronize do
55
+ @@runner.list(response)
56
+ end
57
+ end
58
+ def clear(response)
59
+ @@mutex.synchronize do
60
+ @@runner.clear
61
+ response.reply "cleared all reminders"
62
+ end
63
+ end
64
+
65
+ def time(response)
66
+ response.reply Time.now.to_s
67
+ end
68
+
69
+ def add_domains(response)
70
+ @@mutex.synchronize do
71
+ @@runner.add_domains(response)
72
+ end
73
+ end
74
+
75
+ def delete_domains(response)
76
+ @@mutex.synchronize do
77
+ @@runner.delete_domains(response)
78
+ end
79
+ end
80
+
81
+ def list_domains(response)
82
+ @@mutex.synchronize do
83
+ @@runner.list_domains(response)
84
+ end
85
+ end
86
+
87
+ class << self
88
+ def runner
89
+ @@runner
90
+ end
91
+ end
92
+ end
93
+
94
+ Lita.register_handler(Reminder)
95
+ end
96
+ end
97
+
98
+ # ugly hack
99
+ # TODO fix this better
100
+ module Lita
101
+ class << self
102
+ def run(config_path = nil)
103
+ Config.load_user_config(config_path)
104
+ robot = Robot.new
105
+ redis_base = Redis.new(config.redis)
106
+ redis_ns = Redis::Namespace.new(REDIS_NAMESPACE + ":handlers:reminder", redis: redis_base)
107
+ Lita::Handlers::Reminder.runner.start(robot, redis_ns)
108
+ robot.run
109
+ end
110
+ end
111
+ end
@@ -0,0 +1 @@
1
+ require "lita/handlers/reminder"
@@ -0,0 +1,21 @@
1
+ require "addressable/uri"
2
+
3
+ class ReminderDomain
4
+ attr_reader :domain, :expiration, :whois_data
5
+
6
+ def initialize(domain)
7
+ uri = Addressable::URI.heuristic_parse(domain)
8
+ host = uri.host
9
+ host.start_with?('www.') ? host[4..-1] : host
10
+ @domain = host
11
+ @expiration = expiration
12
+ @reminder = ReminderDomain.task_from_domain(@domain)
13
+ @whois_data = nil
14
+ end
15
+
16
+ class << self
17
+ def task_from_domain(domain)
18
+ Lita::Handlers::Reminder.runner.reminders.select { |task| task.task == "domain expiration #{domain}"}.first
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,72 @@
1
+ class ReminderRunner
2
+ attr_accessor :scheduler, :robot, :reminders, :redis
3
+ def initialize()
4
+ @reminders = []
5
+ @started = false
6
+ end
7
+
8
+ def start(robot, redis)
9
+ @robot = robot
10
+ @redis = redis
11
+ return if @started
12
+ @started = true
13
+ @reminder_count = redis.llen("reminders")
14
+ @scheduler = Rufus::Scheduler.start_new
15
+ @redis.lrange('reminders', 0, -1).each_with_index do |task, index|
16
+ if task.nil? || task == ''
17
+ @reminders[index] = nil
18
+ else
19
+ @reminders << ReminderTask.load(index, task)
20
+ end
21
+ end
22
+ @reminders.each do |task|
23
+ task.start_job(self) unless task.nil?
24
+ end
25
+
26
+ end
27
+
28
+ def add(response)
29
+ task = ReminderTask.from_message(@reminder_count, response.match_data, response.message.source)
30
+ @redis.rpush("reminders", task.dump)
31
+ task.start_job(self)
32
+ @reminders << task
33
+ response.reply("Task #{task.index} added, next run at #{task.job.next_time.strftime('%Y-%m-%d %H:%M:%S')}")
34
+ @reminder_count += 1
35
+ end
36
+
37
+ def done(response)
38
+ @reminders[response.match_data[1]].stop_repeat
39
+ end
40
+
41
+ def delete(response)
42
+ index = kill(response.match_data[1].to_i)
43
+ response.reply("Task #{index} deleted")
44
+ end
45
+
46
+ def list(response)
47
+ reminders = @reminders.reject{|r| r.nil? }
48
+ if reminders.empty?
49
+ response.reply "no active reminders"
50
+ else
51
+ response.reply(reminders.map(&:to_s).join("\n"))
52
+ end
53
+ end
54
+
55
+ def clear
56
+ @redis.ltrim("reminders", 0, 0)
57
+ @reminders.reject{|task| task.nil? }.map(&:die)
58
+ @reminders = []
59
+ @reminder_count = 0
60
+ end
61
+
62
+ def kill(index)
63
+ if index != @reminders[index].index
64
+ raise 'failed sanity check: task has bad index'
65
+ end
66
+ @reminders[index].die
67
+ @reminders[index] = nil
68
+ @redis.lset("reminders", index, nil)
69
+ index
70
+ end
71
+
72
+ end
@@ -0,0 +1,158 @@
1
+ require 'time'
2
+
3
+ class ReminderTask
4
+ attr_accessor :index, :job, :repeat_job, :repeat_count
5
+ def initialize(index, attrs)
6
+ @index = index
7
+ attrs.each_pair do |attr, val|
8
+ send("#{attr.to_s}=".to_sym, val) unless val.nil?
9
+ end
10
+
11
+ end
12
+
13
+ def start_job(handler)
14
+ @handler = handler
15
+ scheduler = @handler.scheduler
16
+
17
+ @periodic = false
18
+ @job =
19
+ # "in" is converted to "at" upon creation
20
+ if type == 'at'
21
+ at = Chronic.parse(@time, now: Time.parse(@c_at))
22
+ if at < Time.now
23
+ run()
24
+ else
25
+ scheduler.at at do
26
+ run()
27
+ end
28
+ end
29
+ elsif type == 'cron'
30
+ @periodic = true
31
+ scheduler.cron @time do
32
+ run()
33
+ end
34
+ elsif type == 'every'
35
+ @periodic = true
36
+ scheduler.every @time, first_at: Time.parse(@first) do
37
+ run()
38
+ end
39
+ end
40
+ end
41
+
42
+ def target
43
+ if user_id.nil? && user_name.nil?
44
+ user = nil
45
+ else
46
+ user = Lita::User.create(user_id, name: user_name)
47
+ end
48
+ Lita::Source.new(user, room)
49
+ end
50
+
51
+ def run
52
+ @handler.robot.send_message(target, message)
53
+ if repeat
54
+ @repeat_job = @handler.scheduler.every repeat_interval do
55
+ repeat()
56
+ end
57
+ elsif !@periodic
58
+ kill
59
+ end
60
+ end
61
+
62
+ def repeat
63
+ @handler.robot.send_message(target, message)
64
+ if repeat != 'many'
65
+ if repeat_count > repeat
66
+ stop_repeat
67
+ end
68
+ end
69
+ end
70
+
71
+ def stop_repeat
72
+ @repeat_job.unschedule
73
+ @repeat_job = nil
74
+ unless @periodic
75
+ kill
76
+ end
77
+ end
78
+
79
+ def kill
80
+ @handler.kill(index)
81
+ end
82
+
83
+ def die
84
+ @job.unschedule unless @job.nil?
85
+ @repeat_job.unschedule unless @repeat_job.nil?
86
+ end
87
+
88
+ def message
89
+ "Reminder #{index}: #{task}"
90
+ end
91
+
92
+ def to_s
93
+ ret = "reminder #{index}, next run: #{@job.nil? ? 'unknown yet' : @job.next_time.to_s} "
94
+ if room.nil? || room == ''
95
+ ret += "user:#{user_id} #{user_name}"
96
+ else
97
+ ret += "room:#{room}"
98
+ end
99
+ ret += " type:#{type} time:#{time}"
100
+ if type == "every"
101
+ ret += " first_at:#{first}"
102
+ end
103
+ ret += " task:#{task}"
104
+ if !repeat.nil? && repeat != ""
105
+ ret += " repeat:#{repeat} times every #{repeat_interval}"
106
+ end
107
+ ret
108
+ end
109
+
110
+ def dump
111
+ hash = {}
112
+ ReminderTask.attrs.each do |attr|
113
+ hash[attr] = send(attr) unless send(attr).nil?
114
+ end
115
+ MultiJson.dump(hash)
116
+ end
117
+
118
+ class << self
119
+ def from_message(index, message, source)
120
+ attrs = {}
121
+ if message['who'] == 'me'
122
+ attrs['user_id'] = source.user.id
123
+ attrs['user_name'] = source.user.name
124
+ elsif message['who'] == 'here'
125
+ attrs['room'] = source.room
126
+ else
127
+ re = /^(user\s+id\s+(?<user_id>.*))?^(user\s+name\s+(?<user_id>.*))?(room \s+(?<room>.*))?$/
128
+ m = re.match(message['who'])
129
+ attrs = attrs.merge(m)
130
+ end
131
+ attrs['type'] = message['type']
132
+ attrs['time'] = message['time']
133
+ attrs['first'] = Chronic.parse(message['first']) unless message['first'].nil?
134
+ attrs['repeat'] = message['repeat']
135
+ attrs['repeat_interval'] = message['repeat_interval'] || '10m'
136
+ attrs['task'] = message['task']
137
+ if attrs['type'] == 'in'
138
+ attrs['type'] = 'at'
139
+ attrs['time'] = (Time.now + Rufus.parse_time_string(attrs['time'])).to_s
140
+ end
141
+ attrs['c_at'] = Time.now.to_s
142
+
143
+ ReminderTask.new index, attrs
144
+ end
145
+ def attrs
146
+ [:c_at, :user_id, :user_name, :room, :type, :time, :first, :task, :repeat, :repeat_interval]
147
+ end
148
+ def load(index, string)
149
+ if string.nil? || string == ''
150
+ nil
151
+ else
152
+ attrs = MultiJson.load(string)
153
+ ReminderTask.new index, attrs
154
+ end
155
+ end
156
+ end
157
+ attr_accessor *ReminderTask.attrs
158
+ end
@@ -0,0 +1,28 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "lita-reminder"
3
+ spec.version = "0.0.1"
4
+ spec.authors = ["glebtv"]
5
+ spec.email = ["glebtv@gmail.com"]
6
+ spec.description = %q{Reminder for Lita chat bot }
7
+ spec.summary = %q{Reminder for Lita chat bot }
8
+ spec.homepage = "https://github.com/glebtv/lita-reminder"
9
+ spec.license = "MIT"
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", "~> 2.4"
17
+ spec.add_runtime_dependency "rufus-scheduler", "~> 2.0.24"
18
+ spec.add_runtime_dependency "chronic", "~> 0.10.2"
19
+ spec.add_runtime_dependency "whois", "~> 3.2.1"
20
+ spec.add_runtime_dependency "addressable", "~> 2.3.5"
21
+
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", ">= 2.14"
26
+ spec.add_development_dependency "simplecov"
27
+ spec.add_development_dependency "coveralls"
28
+ end
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handlers::Reminder, lita_handler: true do
4
+ after :each do
5
+ send_message("reminder clear all")
6
+ end
7
+
8
+ it { routes("remind me at 2013-01-01 10:30 to заплатить за сервер").to(:add) }
9
+ it { routes("reminder delete 1").to(:delete) }
10
+ it { routes("reminder done 1").to(:done) }
11
+ it { routes("remind me at 10:00 to do work repeat 2 times 1m").to(:add) }
12
+ it "adds tasks" do
13
+ send_message("remind me at 2024-01-01 10:30 to заплатить за сервер")
14
+
15
+ index = replies.last.match(/Task (\d+) added/)[1]
16
+ expect(replies.last).to eq("Task #{index} added, next run at 2024-01-01 10:30:00")
17
+ end
18
+
19
+ it "sets user" do
20
+ carl = Lita::User.create(123, name: "Carl")
21
+ send_message("remind me at 2024-01-01 10:30 to заплатить за сервер", as: carl)
22
+
23
+ index = replies.last.match(/Task (\d+) added/)[1]
24
+ expect(replies.last).to eq("Task #{index} added, next run at 2024-01-01 10:30:00")
25
+ end
26
+
27
+ it "deletes tasks" do
28
+ send_message("remind me at 2035-01-01 10:30 to заплатить за сервер")
29
+ index = replies.last.match(/Task (\d+) added/)[1]
30
+ expect(replies.last).to eq("Task #{index} added, next run at 2035-01-01 10:30:00")
31
+ sleep 0.1
32
+ send_message("reminder delete #{index}")
33
+ expect(replies.last).to eq("Task #{index} deleted")
34
+ end
35
+
36
+ it "reminds" do
37
+ send_message("remind me in 1s to заплатить за сервер")
38
+ index = replies.last.match(/Task (\d+) added/)[1]
39
+ expect(replies.last).to eq("Task #{index} added, next run at #{(Time.now + 1).strftime('%Y-%m-%d %H:%M:%S')}")
40
+ sleep 2
41
+ expect(replies.last).to eq("Reminder #{index}: заплатить за сервер")
42
+ end
43
+ end
File without changes
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ describe ReminderTask do
4
+ it 'parses message' do
5
+ carl = Lita::User.create(123, name: "Carl")
6
+ task = ReminderTask.from_message(1, {
7
+ 'who' => 'me',
8
+ 'type' => 'in',
9
+ 'time' => '10m',
10
+ 'repeat' => ''
11
+ }, Lita::Source.new(carl, 'room'))
12
+ task.user_name.should eq carl.name
13
+ task.user_id.should eq carl.id
14
+ task.time.should eq (Time.now + 600).to_s
15
+ task.index.should eq 1
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require "simplecov"
2
+ require "coveralls"
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ Coveralls::SimpleCov::Formatter
6
+ ]
7
+ SimpleCov.start { add_filter "/spec/" }
8
+
9
+ require "lita-reminder"
10
+ require "lita/rspec"
metadata ADDED
@@ -0,0 +1,204 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lita-reminder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - glebtv
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-25 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: '2.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rufus-scheduler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.24
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.24
41
+ - !ruby/object:Gem::Dependency
42
+ name: chronic
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.10.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.10.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: whois
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 3.2.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: addressable
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 2.3.5
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 2.3.5
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '2.14'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '2.14'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: coveralls
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: 'Reminder for Lita chat bot '
154
+ email:
155
+ - glebtv@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - .gitignore
161
+ - .travis.yml
162
+ - Gemfile
163
+ - LICENSE
164
+ - README.md
165
+ - Rakefile
166
+ - lib/lita-reminder.rb
167
+ - lib/lita/handlers/reminder.rb
168
+ - lib/reminder/domain.rb
169
+ - lib/reminder/runner.rb
170
+ - lib/reminder/task.rb
171
+ - lita-reminder.gemspec
172
+ - spec/lita/handlers/reminder_spec.rb
173
+ - spec/reminder/runner_spec.rb
174
+ - spec/reminder/task_spec.rb
175
+ - spec/spec_helper.rb
176
+ homepage: https://github.com/glebtv/lita-reminder
177
+ licenses:
178
+ - MIT
179
+ metadata: {}
180
+ post_install_message:
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - '>='
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.0.6
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: Reminder for Lita chat bot
200
+ test_files:
201
+ - spec/lita/handlers/reminder_spec.rb
202
+ - spec/reminder/runner_spec.rb
203
+ - spec/reminder/task_spec.rb
204
+ - spec/spec_helper.rb