lita-kintai 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gmail.rb +86 -0
- data/lib/lita-kintai.rb +1 -0
- data/lib/lita/handlers/kintai.rb +134 -98
- data/lita-kintai.gemspec +2 -1
- data/spec/lita/handlers/kintai_spec.rb +66 -3
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d058f92110471659693ea8060d40a4a02b85a1f
|
4
|
+
data.tar.gz: 5463d339eaa7b46964df52ca7ab34661f7e9936f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eac814178e90adb59f1c643c261c05403713936c046012cd07e4446bbfd3c83ef2b3965b165f837287b2d41068d72f64f21611536b3764648ead142ddf5a5f63
|
7
|
+
data.tar.gz: afc014be3319b2c57fb72393161be0acc2d12c92c01ef2e5669a7999a1f293b58dbd390424804f5f1d105df72381633fcb4dd2f0c80a3818931405a0106d47b8
|
data/lib/gmail.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'google/apis/gmail_v1'
|
2
|
+
require 'googleauth'
|
3
|
+
require 'googleauth/stores/file_token_store'
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
class Gmail
|
8
|
+
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
|
9
|
+
APPLICATION_NAME = 'Lita Kintai'
|
10
|
+
CLIENT_SECRETS_PATH = 'client_secret.json'
|
11
|
+
CREDENTIALS_PATH = File.join(Dir.home, '.credentials',
|
12
|
+
"lita-kintai.yaml")
|
13
|
+
SCOPE = [Google::Apis::GmailV1::AUTH_GMAIL_READONLY, Google::Apis::GmailV1::AUTH_GMAIL_SEND]
|
14
|
+
USER_ID = 'default'
|
15
|
+
|
16
|
+
def self.authorized?
|
17
|
+
!service.nil? && !service.authorization.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.credentials_from_code(code)
|
21
|
+
authorizer.get_and_store_credentials_from_code(
|
22
|
+
user_id: USER_ID, code: code, base_url: OOB_URI)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.authorization_url
|
26
|
+
authorizer.get_authorization_url(base_url: OOB_URI)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.find_mail(query)
|
30
|
+
ids = service.list_user_messages('me', q: query)
|
31
|
+
|
32
|
+
return [] unless ids.messages
|
33
|
+
ids.messages.map do |message|
|
34
|
+
find_mail_by_id(message.id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.send_message(mail)
|
39
|
+
message = Google::Apis::GmailV1::Message.new(raw: mail.to_s )
|
40
|
+
result = service.send_user_message('me', message)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def self.service
|
45
|
+
return @service if !@service.nil? && !@service.authorization.nil?
|
46
|
+
|
47
|
+
credentials = authorizer.get_credentials(USER_ID)
|
48
|
+
if credentials
|
49
|
+
@service.authorization = credentials
|
50
|
+
return @service
|
51
|
+
end
|
52
|
+
|
53
|
+
return @service
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.authorizer
|
57
|
+
return @authorizer unless @authorizer.nil?
|
58
|
+
|
59
|
+
@service = Google::Apis::GmailV1::GmailService.new
|
60
|
+
@service.client_options.application_name = APPLICATION_NAME
|
61
|
+
|
62
|
+
FileUtils.mkdir_p(File.dirname(CREDENTIALS_PATH))
|
63
|
+
|
64
|
+
client_id = Google::Auth::ClientId.from_file(CLIENT_SECRETS_PATH)
|
65
|
+
token_store = Google::Auth::Stores::FileTokenStore.new(
|
66
|
+
file: CREDENTIALS_PATH)
|
67
|
+
@authorizer = Google::Auth::UserAuthorizer.new(
|
68
|
+
client_id, SCOPE, token_store)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.find_mail_by_id(id)
|
72
|
+
results = @service.get_user_message('me', id)
|
73
|
+
|
74
|
+
body = results.payload.parts ?
|
75
|
+
results.payload.parts.first.body.data :
|
76
|
+
results.payload.body.data
|
77
|
+
headers = results.payload.headers
|
78
|
+
|
79
|
+
{
|
80
|
+
subject: headers.select { |e| e.name == 'Subject'}.first.value,
|
81
|
+
from: headers.select { |e| e.name == 'From'}.first.value,
|
82
|
+
date: Time.parse(headers.select { |e| e.name == 'Date'}.first.value),
|
83
|
+
body: body.force_encoding('utf-8'),
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
data/lib/lita-kintai.rb
CHANGED
data/lib/lita/handlers/kintai.rb
CHANGED
@@ -1,41 +1,52 @@
|
|
1
|
-
require 'google/apis/gmail_v1'
|
2
|
-
require 'googleauth'
|
3
|
-
require 'googleauth/stores/file_token_store'
|
4
|
-
|
5
|
-
require 'fileutils'
|
6
1
|
require 'date'
|
7
|
-
|
8
2
|
require 'rufus-scheduler'
|
3
|
+
require 'mail'
|
9
4
|
|
10
5
|
module Lita
|
11
6
|
module Handlers
|
12
7
|
class Kintai < Handler
|
13
8
|
config :query, type: String
|
9
|
+
config :mail_to, type: String, default: ''
|
10
|
+
config :mail_cc, type: String, default: ''
|
11
|
+
config :template_subject, type: String, default: ''
|
14
12
|
config :template_header, type: String, default: ''
|
15
13
|
config :template_footer, type: String, default: ''
|
14
|
+
config :template_info, type: String, default: ''
|
16
15
|
config :schedule_cron, type: String, default: nil
|
17
16
|
config :schedule_room, type: String, default: nil
|
18
17
|
|
19
18
|
route /kintai/i, :kintai, command: true
|
19
|
+
route /^draft\s+(.+)/im, :draft, command: true
|
20
20
|
route /^code\s+(.+)/, :code, command: true
|
21
21
|
|
22
22
|
on :loaded, :load_on_start
|
23
|
-
|
24
|
-
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
|
25
|
-
APPLICATION_NAME = 'Lita Kintai'
|
26
|
-
CLIENT_SECRETS_PATH = 'client_secret.json'
|
27
|
-
CREDENTIALS_PATH = File.join(Dir.home, '.credentials',
|
28
|
-
"lita-kintai.yaml")
|
29
|
-
SCOPE = Google::Apis::GmailV1::AUTH_GMAIL_READONLY
|
30
|
-
USER_ID = 'default'
|
23
|
+
on :slack_reaction_added, :reaction_added
|
31
24
|
|
32
25
|
def kintai(response)
|
33
|
-
|
26
|
+
if Gmail.authorized?
|
27
|
+
register_draft(response, kintai_info)
|
28
|
+
else
|
29
|
+
response.reply(authenticate_info)
|
30
|
+
end
|
34
31
|
end
|
35
32
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
33
|
+
def draft(response)
|
34
|
+
info = response.matches[0][0]
|
35
|
+
register_draft(response, info)
|
36
|
+
end
|
37
|
+
|
38
|
+
def register_draft(response, info)
|
39
|
+
mail = create_kintai_mail(info)
|
40
|
+
reply = response.reply(mail_to_message(mail))
|
41
|
+
@@draft = { channel: reply["channel"], ts: reply["ts"], mail: mail }
|
42
|
+
reply
|
43
|
+
end
|
44
|
+
|
45
|
+
def code(response)
|
46
|
+
code = response.matches[0][0]
|
47
|
+
Gmail.credentials_from_code(code)
|
48
|
+
|
49
|
+
response.reply("Confirmed")
|
39
50
|
end
|
40
51
|
|
41
52
|
def load_on_start(_payload)
|
@@ -51,119 +62,144 @@ module Lita
|
|
51
62
|
end
|
52
63
|
end
|
53
64
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
65
|
+
def send_kintai(user: user, room: room)
|
66
|
+
if Gmail.authorized?
|
67
|
+
mail = create_kintai_mail(kintai_info)
|
68
|
+
reply = send_message(user: user, room: room, message: mail)
|
69
|
+
@@draft = { channel: reply["channel"], ts: reply["ts"], mail: mail }
|
70
|
+
reply
|
71
|
+
else
|
72
|
+
send_message(user: user, room: room, message: authenticate_info)
|
73
|
+
end
|
74
|
+
end
|
58
75
|
|
59
|
-
|
76
|
+
def reaction_added(_payload)
|
77
|
+
case _payload[:name]
|
78
|
+
when '+1' then
|
79
|
+
if !@@draft.nil? &&
|
80
|
+
_payload[:item]["type"] == "message" &&
|
81
|
+
_payload[:item]["channel"] == @@draft[:channel] &&
|
82
|
+
_payload[:item]["ts"] == @@draft[:ts]
|
83
|
+
if Gmail.authorized?
|
84
|
+
# TODO: 成功失敗
|
85
|
+
send_mail(@@draft[:mail])
|
86
|
+
@@draft = nil
|
87
|
+
send_message(room: _payload[:item]["channel"],
|
88
|
+
message: 'Sent email.')
|
89
|
+
else
|
90
|
+
send_message(room: _payload[:item]["channel"],
|
91
|
+
message: authenticate_info)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
60
95
|
end
|
61
96
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
97
|
+
def send_message(user: user, room: room, message: message)
|
98
|
+
target = Source.new(user: user, room: room)
|
99
|
+
robot.send_message(target, message)
|
100
|
+
end
|
101
|
+
|
102
|
+
def create_kintai_mail(info)
|
103
|
+
create_mail(
|
104
|
+
to: config.mail_to,
|
105
|
+
cc: config.mail_cc,
|
106
|
+
subject: kintai_subject,
|
107
|
+
body: kintai_body(info)
|
108
|
+
)
|
109
|
+
end
|
68
110
|
|
69
|
-
|
70
|
-
|
111
|
+
def mail_to_message(mail)
|
112
|
+
<<-EOS
|
113
|
+
To: #{mail.to}
|
114
|
+
Cc: #{mail.cc}
|
115
|
+
Subject: #{mail.subject}
|
116
|
+
#{mail.body.to_s}
|
117
|
+
EOS
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_mail(to: to, cc: cc, subject: subject, body: body)
|
121
|
+
Mail.new do
|
122
|
+
to to
|
123
|
+
cc cc
|
124
|
+
subject subject
|
125
|
+
body body
|
71
126
|
end
|
127
|
+
end
|
72
128
|
|
73
|
-
|
129
|
+
def send_mail(mail)
|
130
|
+
return Gmail.send_message(mail)
|
74
131
|
end
|
75
132
|
|
76
133
|
def kintai_info
|
77
134
|
texts = ""
|
78
|
-
texts << "#{Date.today.strftime("%m/%d")} (#{%w(日 月 火 水 木 金 土)[Date.today.wday]})#{config.template_header}"
|
79
135
|
|
80
|
-
mails = find_mail(config.query)
|
136
|
+
mails = Gmail.find_mail(config.query)
|
81
137
|
# query の `newer:#{Date.today.strftime("%Y/%m/%d")}` 昨日のも一部返ってくる
|
82
138
|
# `newer_than:1d` だと24h以内になるので、ここで今日のだけにする
|
83
139
|
mails.select{ |m| m[:date] > Date.today.to_time }.each do |m|
|
84
140
|
name = m[:from].split("\"")[1]
|
85
141
|
|
86
142
|
text = m[:subject] + m[:body]
|
143
|
+
info = kintai_from_text(text)
|
87
144
|
|
88
|
-
|
89
|
-
if text.match(/電車|列車/)
|
90
|
-
reason = "電車遅延のため、"
|
91
|
-
end
|
92
|
-
if text.match(/体調|痛/)
|
93
|
-
reason = "体調不良のため、"
|
94
|
-
end
|
95
|
-
if text.match(/健康診断|検診|健診/)
|
96
|
-
reason = "健康診断のため、"
|
97
|
-
end
|
98
|
-
|
99
|
-
at = "出社時刻未定です。"
|
100
|
-
if hm = text.match(/([0-1][0-9]|[2][0-3]):[0-5][0-9]/)
|
101
|
-
at = "#{hm}頃出社予定です。"
|
102
|
-
elsif min = text.match(/([0-5][0-9])分/)
|
103
|
-
at = "10:#{min[1]}頃出社予定です。"
|
104
|
-
end
|
105
|
-
|
106
|
-
if text.match(/おやすみ|休み|有給|休暇/)
|
107
|
-
reason = "本日お休みです。"
|
108
|
-
at = ""
|
109
|
-
end
|
110
|
-
|
111
|
-
texts << "#{name}さん: #{reason}#{at}\n"
|
145
|
+
texts << "#{name}さん: #{info}\n"
|
112
146
|
end
|
113
|
-
|
114
|
-
texts << config.template_footer
|
147
|
+
texts << config.template_info
|
115
148
|
end
|
116
149
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
if credentials
|
122
|
-
@service.authorization = credentials
|
123
|
-
return @service
|
124
|
-
end
|
125
|
-
|
126
|
-
return nil
|
150
|
+
def self.kintai_from_text(text)
|
151
|
+
reason = kintai_reason(text)
|
152
|
+
time = kintai_time(text)
|
153
|
+
"#{reason}のため、#{time}です。"
|
127
154
|
end
|
128
155
|
|
129
|
-
def
|
130
|
-
|
156
|
+
def kintai_subject
|
157
|
+
"#{Date.today.strftime("%m/%d")} (#{%w(日 月 火 水 木 金 土)[Date.today.wday]})#{config.template_subject}"
|
158
|
+
end
|
131
159
|
|
132
|
-
|
133
|
-
|
160
|
+
def kintai_body(info)
|
161
|
+
<<-EOS
|
162
|
+
#{config.template_header}
|
134
163
|
|
135
|
-
|
164
|
+
#{info}
|
136
165
|
|
137
|
-
|
138
|
-
|
139
|
-
file: CREDENTIALS_PATH)
|
140
|
-
@authorizer = Google::Auth::UserAuthorizer.new(
|
141
|
-
client_id, SCOPE, token_store)
|
166
|
+
#{config.template_footer}
|
167
|
+
EOS
|
142
168
|
end
|
143
169
|
|
144
|
-
def
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
170
|
+
def self.kintai_reason(text)
|
171
|
+
if text.match(/電車|列車/)
|
172
|
+
return "電車遅延"
|
173
|
+
elsif text.match(/体調|痛/)
|
174
|
+
return "体調不良"
|
175
|
+
elsif text.match(/健康診断|検診|健診/)
|
176
|
+
return "健康診断"
|
150
177
|
end
|
178
|
+
return "私用"
|
151
179
|
end
|
152
180
|
|
153
|
-
def
|
154
|
-
|
181
|
+
def self.kintai_time(text)
|
182
|
+
if hm = text.match(/([0-1][0-9]|[2][0-3]):[0-5][0-9]/)
|
183
|
+
return "#{hm}頃出社予定"
|
184
|
+
elsif min = text.match(/(([0-5])*[0-9])分/)
|
185
|
+
return "10:#{min[1].rjust(2, "0")}頃出社予定"
|
186
|
+
elsif half = text.match(/([0-1][0-9]|[2][0-3])時半/)
|
187
|
+
return "#{half[1]}:30頃出社予定"
|
188
|
+
elsif half = text.match(/(\d)時間半/)
|
189
|
+
return "#{10+half[1].to_i}:30頃出社予定"
|
190
|
+
elsif text.match(/おやすみ|休み|有給|休暇/)
|
191
|
+
return "本日お休み"
|
192
|
+
end
|
193
|
+
return "出社時刻未定"
|
194
|
+
end
|
155
195
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
196
|
+
def authenticate_info
|
197
|
+
<<-EOS
|
198
|
+
Authenticate your Google account.
|
199
|
+
Then tell me the code as follows: `code \#{your_code}`
|
160
200
|
|
161
|
-
|
162
|
-
|
163
|
-
from: headers.select { |e| e.name == 'From'}.first.value,
|
164
|
-
date: Time.parse(headers.select { |e| e.name == 'Date'}.first.value),
|
165
|
-
body: body.force_encoding('utf-8'),
|
166
|
-
}
|
201
|
+
#{Gmail.authorization_url}
|
202
|
+
EOS
|
167
203
|
end
|
168
204
|
|
169
205
|
Lita.register_handler(self)
|
data/lita-kintai.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "lita-kintai"
|
3
|
-
spec.version = "0.
|
3
|
+
spec.version = "0.3.0"
|
4
4
|
spec.authors = ["Shoma SATO"]
|
5
5
|
spec.email = ["noir.neo.04@gmail.com"]
|
6
6
|
spec.description = "A lita handler for summarize attendance emails. Fuckin' legacy attendance management method by email!"
|
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.add_runtime_dependency "lita", ">= 4.7"
|
18
18
|
spec.add_runtime_dependency "google-api-client"
|
19
19
|
spec.add_runtime_dependency "rufus-scheduler"
|
20
|
+
spec.add_runtime_dependency "mail"
|
20
21
|
|
21
22
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
23
|
spec.add_development_dependency "pry-byebug"
|
@@ -9,8 +9,8 @@ describe Lita::Handlers::Kintai, lita_handler: true do
|
|
9
9
|
describe '#kintai' do
|
10
10
|
context 'when authenticated' do
|
11
11
|
before do
|
12
|
-
|
13
|
-
|
12
|
+
allow(Gmail).to receive(:authorized?).and_return(true)
|
13
|
+
allow(Gmail).to receive(:find_mail).and_return(
|
14
14
|
[
|
15
15
|
{
|
16
16
|
subject: '',
|
@@ -22,10 +22,73 @@ describe Lita::Handlers::Kintai, lita_handler: true do
|
|
22
22
|
)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
xit 'returns kintai list' do
|
26
26
|
send_command('kintai')
|
27
27
|
expect(replies.last).not_to be_nil
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
describe '#kintai_reason(text)' do
|
33
|
+
subject { Lita::Handlers::Kintai.kintai_reason(text) }
|
34
|
+
|
35
|
+
context '「電車」が含まれる時' do
|
36
|
+
let(:text) { "電車遅延のため" }
|
37
|
+
it { is_expected.to eq "電車遅延" }
|
38
|
+
end
|
39
|
+
|
40
|
+
context '「体調不良」が含まれる時' do
|
41
|
+
let(:text) { "体調不良のため" }
|
42
|
+
it { is_expected.to eq "体調不良" }
|
43
|
+
end
|
44
|
+
|
45
|
+
context '「健康診断」が含まれる時' do
|
46
|
+
let(:text) { "健康診断に行くので" }
|
47
|
+
it { is_expected.to eq "健康診断" }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'いずれにもマッチしない時' do
|
51
|
+
let(:text) { "寝坊したので" }
|
52
|
+
it { is_expected.to eq "私用" }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#kintai_time(text)' do
|
57
|
+
subject { Lita::Handlers::Kintai.kintai_time(text) }
|
58
|
+
|
59
|
+
context '「HH:mm」が含まれる時' do
|
60
|
+
let(:text) { "12:00頃出社します" }
|
61
|
+
it { is_expected.to eq "12:00頃出社予定" }
|
62
|
+
end
|
63
|
+
|
64
|
+
context '「mm分」が含まれる時' do
|
65
|
+
let(:text) { "10分ほど遅れます" }
|
66
|
+
it { is_expected.to eq "10:10頃出社予定" }
|
67
|
+
end
|
68
|
+
|
69
|
+
context '「m分」が含まれる時' do
|
70
|
+
let(:text) { "5分ほど遅れます" }
|
71
|
+
it { is_expected.to eq "10:05頃出社予定" }
|
72
|
+
end
|
73
|
+
|
74
|
+
context '「HH時半」が含まれる時' do
|
75
|
+
let(:text) { "10時半ごろに出社します" }
|
76
|
+
it { is_expected.to eq "10:30頃出社予定" }
|
77
|
+
end
|
78
|
+
|
79
|
+
context '「n時間半」が含まれる時' do
|
80
|
+
let(:text) { "1時間半ほど遅れます" }
|
81
|
+
it { is_expected.to eq "11:30頃出社予定" }
|
82
|
+
end
|
83
|
+
|
84
|
+
context '「休み」が含まれる時' do
|
85
|
+
let(:text) { "お休みをいただいてます" }
|
86
|
+
it { is_expected.to eq "本日お休み" }
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'いずれにもマッチしない時' do
|
90
|
+
let(:text) { "出社時刻わかり次第また連絡します" }
|
91
|
+
it { is_expected.to eq "出社時刻未定" }
|
92
|
+
end
|
93
|
+
end
|
31
94
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lita-kintai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shoma SATO
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lita
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mail
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +148,7 @@ files:
|
|
134
148
|
- Gemfile
|
135
149
|
- README.md
|
136
150
|
- Rakefile
|
151
|
+
- lib/gmail.rb
|
137
152
|
- lib/lita-kintai.rb
|
138
153
|
- lib/lita/handlers/kintai.rb
|
139
154
|
- lita-kintai.gemspec
|