mcpeasy 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claudeignore +0 -3
- data/.mcp.json +19 -1
- data/CHANGELOG.md +59 -0
- data/CLAUDE.md +19 -5
- data/README.md +19 -3
- data/lib/mcpeasy/cli.rb +62 -10
- data/lib/mcpeasy/config.rb +22 -1
- data/lib/mcpeasy/setup.rb +1 -0
- data/lib/mcpeasy/version.rb +1 -1
- data/lib/utilities/gcal/README.md +11 -3
- data/lib/utilities/gcal/cli.rb +110 -108
- data/lib/utilities/gcal/mcp.rb +463 -308
- data/lib/utilities/gcal/service.rb +312 -0
- data/lib/utilities/gdrive/README.md +3 -3
- data/lib/utilities/gdrive/cli.rb +98 -96
- data/lib/utilities/gdrive/mcp.rb +290 -288
- data/lib/utilities/gdrive/service.rb +293 -0
- data/lib/utilities/gmail/README.md +278 -0
- data/lib/utilities/gmail/cli.rb +264 -0
- data/lib/utilities/gmail/mcp.rb +846 -0
- data/lib/utilities/gmail/service.rb +547 -0
- data/lib/utilities/gmeet/cli.rb +131 -129
- data/lib/utilities/gmeet/mcp.rb +374 -372
- data/lib/utilities/gmeet/service.rb +411 -0
- data/lib/utilities/notion/README.md +287 -0
- data/lib/utilities/notion/cli.rb +245 -0
- data/lib/utilities/notion/mcp.rb +607 -0
- data/lib/utilities/notion/service.rb +327 -0
- data/lib/utilities/slack/README.md +3 -3
- data/lib/utilities/slack/cli.rb +69 -54
- data/lib/utilities/slack/mcp.rb +277 -226
- data/lib/utilities/slack/service.rb +134 -0
- data/mcpeasy.gemspec +6 -1
- metadata +87 -10
- data/env.template +0 -11
- data/lib/utilities/gcal/gcal_tool.rb +0 -308
- data/lib/utilities/gdrive/gdrive_tool.rb +0 -291
- data/lib/utilities/gmeet/gmeet_tool.rb +0 -407
- data/lib/utilities/slack/slack_tool.rb +0 -119
- data/logs/.keep +0 -0
@@ -0,0 +1,547 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "google/apis/gmail_v1"
|
5
|
+
require "googleauth"
|
6
|
+
require "signet/oauth_2/client"
|
7
|
+
require "fileutils"
|
8
|
+
require "json"
|
9
|
+
require "time"
|
10
|
+
require "mail"
|
11
|
+
require "html2text"
|
12
|
+
require_relative "../_google/auth_server"
|
13
|
+
require_relative "../../mcpeasy/config"
|
14
|
+
|
15
|
+
module Gmail
|
16
|
+
class Service
|
17
|
+
SCOPES = [
|
18
|
+
"https://www.googleapis.com/auth/gmail.readonly",
|
19
|
+
"https://www.googleapis.com/auth/gmail.send",
|
20
|
+
"https://www.googleapis.com/auth/gmail.modify"
|
21
|
+
]
|
22
|
+
SCOPE = SCOPES.join(" ")
|
23
|
+
|
24
|
+
def initialize(skip_auth: false)
|
25
|
+
ensure_env!
|
26
|
+
@service = Google::Apis::GmailV1::GmailService.new
|
27
|
+
@service.authorization = authorize unless skip_auth
|
28
|
+
end
|
29
|
+
|
30
|
+
def list_emails(start_date: nil, end_date: nil, max_results: 20, sender: nil, subject: nil, labels: nil, read_status: nil)
|
31
|
+
query_parts = []
|
32
|
+
|
33
|
+
# Date filtering
|
34
|
+
if start_date
|
35
|
+
query_parts << "after:#{start_date}"
|
36
|
+
end
|
37
|
+
if end_date
|
38
|
+
query_parts << "before:#{end_date}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sender filtering
|
42
|
+
if sender
|
43
|
+
query_parts << "from:#{sender}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Subject filtering
|
47
|
+
if subject
|
48
|
+
query_parts << "subject:\"#{subject}\""
|
49
|
+
end
|
50
|
+
|
51
|
+
# Labels filtering
|
52
|
+
if labels && !labels.empty?
|
53
|
+
if labels.is_a?(Array)
|
54
|
+
labels.each { |label| query_parts << "label:#{label}" }
|
55
|
+
else
|
56
|
+
query_parts << "label:#{labels}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Read/unread status
|
61
|
+
case read_status&.downcase
|
62
|
+
when "unread"
|
63
|
+
query_parts << "is:unread"
|
64
|
+
when "read"
|
65
|
+
query_parts << "is:read"
|
66
|
+
end
|
67
|
+
|
68
|
+
query = query_parts.join(" ")
|
69
|
+
|
70
|
+
results = @service.list_user_messages(
|
71
|
+
"me",
|
72
|
+
q: query.empty? ? nil : query,
|
73
|
+
max_results: max_results
|
74
|
+
)
|
75
|
+
|
76
|
+
emails = (results.messages || []).map do |message|
|
77
|
+
get_email_summary(message.id)
|
78
|
+
end.compact
|
79
|
+
|
80
|
+
{emails: emails, count: emails.length}
|
81
|
+
rescue Google::Apis::Error => e
|
82
|
+
raise "Gmail API Error: #{e.message}"
|
83
|
+
rescue => e
|
84
|
+
log_error("list_emails", e)
|
85
|
+
raise e
|
86
|
+
end
|
87
|
+
|
88
|
+
def search_emails(query, max_results: 10)
|
89
|
+
results = @service.list_user_messages(
|
90
|
+
"me",
|
91
|
+
q: query,
|
92
|
+
max_results: max_results
|
93
|
+
)
|
94
|
+
|
95
|
+
emails = (results.messages || []).map do |message|
|
96
|
+
get_email_summary(message.id)
|
97
|
+
end.compact
|
98
|
+
|
99
|
+
{emails: emails, count: emails.length}
|
100
|
+
rescue Google::Apis::Error => e
|
101
|
+
raise "Gmail API Error: #{e.message}"
|
102
|
+
rescue => e
|
103
|
+
log_error("search_emails", e)
|
104
|
+
raise e
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_email_content(email_id)
|
108
|
+
message = @service.get_user_message("me", email_id)
|
109
|
+
|
110
|
+
# Extract email metadata
|
111
|
+
headers = extract_headers(message.payload)
|
112
|
+
|
113
|
+
# Extract email body
|
114
|
+
body_data = extract_body(message.payload)
|
115
|
+
|
116
|
+
{
|
117
|
+
id: message.id,
|
118
|
+
thread_id: message.thread_id,
|
119
|
+
subject: headers["Subject"],
|
120
|
+
from: headers["From"],
|
121
|
+
to: headers["To"],
|
122
|
+
cc: headers["Cc"],
|
123
|
+
bcc: headers["Bcc"],
|
124
|
+
date: headers["Date"],
|
125
|
+
body: body_data[:text],
|
126
|
+
body_html: body_data[:html],
|
127
|
+
snippet: message.snippet,
|
128
|
+
labels: message.label_ids || [],
|
129
|
+
attachments: extract_attachments(message.payload)
|
130
|
+
}
|
131
|
+
rescue Google::Apis::Error => e
|
132
|
+
raise "Gmail API Error: #{e.message}"
|
133
|
+
rescue => e
|
134
|
+
log_error("get_email_content", e)
|
135
|
+
raise e
|
136
|
+
end
|
137
|
+
|
138
|
+
def send_email(to:, subject:, body:, cc: nil, bcc: nil, reply_to: nil)
|
139
|
+
mail = Mail.new do
|
140
|
+
to to
|
141
|
+
cc cc if cc
|
142
|
+
bcc bcc if bcc
|
143
|
+
subject subject
|
144
|
+
body body
|
145
|
+
reply_to reply_to if reply_to
|
146
|
+
end
|
147
|
+
|
148
|
+
raw_message = mail.to_s
|
149
|
+
encoded_message = Base64.urlsafe_encode64(raw_message)
|
150
|
+
|
151
|
+
message_object = Google::Apis::GmailV1::Message.new(raw: encoded_message)
|
152
|
+
result = @service.send_user_message("me", message_object)
|
153
|
+
|
154
|
+
{
|
155
|
+
success: true,
|
156
|
+
message_id: result.id,
|
157
|
+
thread_id: result.thread_id
|
158
|
+
}
|
159
|
+
rescue Google::Apis::Error => e
|
160
|
+
raise "Gmail API Error: #{e.message}"
|
161
|
+
rescue => e
|
162
|
+
log_error("send_email", e)
|
163
|
+
raise e
|
164
|
+
end
|
165
|
+
|
166
|
+
def reply_to_email(email_id:, body:, include_quoted: true)
|
167
|
+
# Get the original message to extract reply information
|
168
|
+
original = @service.get_user_message("me", email_id)
|
169
|
+
headers = extract_headers(original.payload)
|
170
|
+
|
171
|
+
# Build reply headers
|
172
|
+
original_subject = headers["Subject"] || ""
|
173
|
+
reply_subject = original_subject.start_with?("Re:") ? original_subject : "Re: #{original_subject}"
|
174
|
+
|
175
|
+
reply_to = headers["Reply-To"] || headers["From"]
|
176
|
+
message_id = headers["Message-ID"]
|
177
|
+
|
178
|
+
# Prepare reply body
|
179
|
+
reply_body = body
|
180
|
+
if include_quoted
|
181
|
+
original_body = extract_body(original.payload)[:text]
|
182
|
+
date_str = headers["Date"]
|
183
|
+
from_str = headers["From"]
|
184
|
+
|
185
|
+
reply_body += "\n\n"
|
186
|
+
reply_body += "On #{date_str}, #{from_str} wrote:\n"
|
187
|
+
reply_body += original_body.split("\n").map { |line| "> #{line}" }.join("\n")
|
188
|
+
end
|
189
|
+
|
190
|
+
mail = Mail.new do
|
191
|
+
to reply_to
|
192
|
+
subject reply_subject
|
193
|
+
body reply_body
|
194
|
+
in_reply_to message_id if message_id
|
195
|
+
references message_id if message_id
|
196
|
+
end
|
197
|
+
|
198
|
+
raw_message = mail.to_s
|
199
|
+
encoded_message = Base64.urlsafe_encode64(raw_message)
|
200
|
+
|
201
|
+
message_object = Google::Apis::GmailV1::Message.new(
|
202
|
+
raw: encoded_message,
|
203
|
+
thread_id: original.thread_id
|
204
|
+
)
|
205
|
+
|
206
|
+
result = @service.send_user_message("me", message_object)
|
207
|
+
|
208
|
+
{
|
209
|
+
success: true,
|
210
|
+
message_id: result.id,
|
211
|
+
thread_id: result.thread_id
|
212
|
+
}
|
213
|
+
rescue Google::Apis::Error => e
|
214
|
+
raise "Gmail API Error: #{e.message}"
|
215
|
+
rescue => e
|
216
|
+
log_error("reply_to_email", e)
|
217
|
+
raise e
|
218
|
+
end
|
219
|
+
|
220
|
+
def mark_as_read(email_id)
|
221
|
+
modify_message(email_id, remove_label_ids: ["UNREAD"])
|
222
|
+
end
|
223
|
+
|
224
|
+
def mark_as_unread(email_id)
|
225
|
+
modify_message(email_id, add_label_ids: ["UNREAD"])
|
226
|
+
end
|
227
|
+
|
228
|
+
def add_label(email_id, label)
|
229
|
+
label_id = resolve_label_id(label)
|
230
|
+
modify_message(email_id, add_label_ids: [label_id])
|
231
|
+
end
|
232
|
+
|
233
|
+
def remove_label(email_id, label)
|
234
|
+
label_id = resolve_label_id(label)
|
235
|
+
modify_message(email_id, remove_label_ids: [label_id])
|
236
|
+
end
|
237
|
+
|
238
|
+
def archive_email(email_id)
|
239
|
+
modify_message(email_id, remove_label_ids: ["INBOX"])
|
240
|
+
end
|
241
|
+
|
242
|
+
def trash_email(email_id)
|
243
|
+
@service.trash_user_message("me", email_id)
|
244
|
+
{success: true}
|
245
|
+
rescue Google::Apis::Error => e
|
246
|
+
raise "Gmail API Error: #{e.message}"
|
247
|
+
rescue => e
|
248
|
+
log_error("trash_email", e)
|
249
|
+
raise e
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_connection
|
253
|
+
profile = @service.get_user_profile("me")
|
254
|
+
{
|
255
|
+
ok: true,
|
256
|
+
email: profile.email_address,
|
257
|
+
messages_total: profile.messages_total,
|
258
|
+
threads_total: profile.threads_total
|
259
|
+
}
|
260
|
+
rescue Google::Apis::Error => e
|
261
|
+
raise "Gmail API Error: #{e.message}"
|
262
|
+
rescue => e
|
263
|
+
log_error("test_connection", e)
|
264
|
+
raise e
|
265
|
+
end
|
266
|
+
|
267
|
+
def authenticate
|
268
|
+
perform_auth_flow
|
269
|
+
{success: true}
|
270
|
+
rescue => e
|
271
|
+
{success: false, error: e.message}
|
272
|
+
end
|
273
|
+
|
274
|
+
def perform_auth_flow
|
275
|
+
client_id = Mcpeasy::Config.google_client_id
|
276
|
+
client_secret = Mcpeasy::Config.google_client_secret
|
277
|
+
|
278
|
+
unless client_id && client_secret
|
279
|
+
raise "Google credentials not found. Please save your credentials.json file using: mcpz config set_google_credentials <path_to_credentials.json>"
|
280
|
+
end
|
281
|
+
|
282
|
+
# Create credentials using OAuth2 flow with localhost redirect
|
283
|
+
redirect_uri = "http://localhost:8080"
|
284
|
+
client = Signet::OAuth2::Client.new(
|
285
|
+
client_id: client_id,
|
286
|
+
client_secret: client_secret,
|
287
|
+
scope: SCOPE,
|
288
|
+
redirect_uri: redirect_uri,
|
289
|
+
authorization_uri: "https://accounts.google.com/o/oauth2/auth",
|
290
|
+
token_credential_uri: "https://oauth2.googleapis.com/token"
|
291
|
+
)
|
292
|
+
|
293
|
+
# Generate authorization URL
|
294
|
+
url = client.authorization_uri.to_s
|
295
|
+
|
296
|
+
puts "DEBUG: Client ID: #{client_id[0..20]}..."
|
297
|
+
puts "DEBUG: Scope: #{SCOPE}"
|
298
|
+
puts "DEBUG: Redirect URI: #{redirect_uri}"
|
299
|
+
puts
|
300
|
+
|
301
|
+
# Start callback server to capture OAuth code
|
302
|
+
puts "Starting temporary web server to capture OAuth callback..."
|
303
|
+
puts "Opening authorization URL in your default browser..."
|
304
|
+
puts url
|
305
|
+
puts
|
306
|
+
|
307
|
+
# Automatically open URL in default browser on macOS/Unix
|
308
|
+
if system("which open > /dev/null 2>&1")
|
309
|
+
system("open", url)
|
310
|
+
else
|
311
|
+
puts "Could not automatically open browser. Please copy the URL above manually."
|
312
|
+
end
|
313
|
+
puts
|
314
|
+
puts "Waiting for OAuth callback... (will timeout in 60 seconds)"
|
315
|
+
|
316
|
+
# Wait for the authorization code with timeout
|
317
|
+
code = GoogleAuthServer.capture_auth_code
|
318
|
+
|
319
|
+
unless code
|
320
|
+
raise "Failed to receive authorization code. Please try again."
|
321
|
+
end
|
322
|
+
|
323
|
+
puts "✅ Authorization code received!"
|
324
|
+
client.code = code
|
325
|
+
client.fetch_access_token!
|
326
|
+
|
327
|
+
# Save credentials to config
|
328
|
+
credentials_data = {
|
329
|
+
client_id: client.client_id,
|
330
|
+
client_secret: client.client_secret,
|
331
|
+
scope: client.scope,
|
332
|
+
refresh_token: client.refresh_token,
|
333
|
+
access_token: client.access_token,
|
334
|
+
expires_at: client.expires_at
|
335
|
+
}
|
336
|
+
|
337
|
+
Mcpeasy::Config.save_google_token(credentials_data)
|
338
|
+
puts "✅ Authentication successful! Token saved to config"
|
339
|
+
|
340
|
+
client
|
341
|
+
rescue => e
|
342
|
+
log_error("perform_auth_flow", e)
|
343
|
+
raise "Authentication flow failed: #{e.message}"
|
344
|
+
end
|
345
|
+
|
346
|
+
private
|
347
|
+
|
348
|
+
def authorize
|
349
|
+
credentials_data = Mcpeasy::Config.google_token
|
350
|
+
unless credentials_data
|
351
|
+
raise <<~ERROR
|
352
|
+
Gmail authentication required!
|
353
|
+
Run the auth command first:
|
354
|
+
mcpz gmail auth
|
355
|
+
ERROR
|
356
|
+
end
|
357
|
+
|
358
|
+
client = Signet::OAuth2::Client.new(
|
359
|
+
client_id: credentials_data.client_id,
|
360
|
+
client_secret: credentials_data.client_secret,
|
361
|
+
scope: credentials_data.scope.respond_to?(:to_a) ? credentials_data.scope.to_a.join(" ") : credentials_data.scope.to_s,
|
362
|
+
refresh_token: credentials_data.refresh_token,
|
363
|
+
access_token: credentials_data.access_token,
|
364
|
+
token_credential_uri: "https://oauth2.googleapis.com/token"
|
365
|
+
)
|
366
|
+
|
367
|
+
# Check if token needs refresh
|
368
|
+
if credentials_data.expires_at
|
369
|
+
expires_at = if credentials_data.expires_at.is_a?(String)
|
370
|
+
Time.parse(credentials_data.expires_at)
|
371
|
+
else
|
372
|
+
Time.at(credentials_data.expires_at)
|
373
|
+
end
|
374
|
+
|
375
|
+
if Time.now >= expires_at
|
376
|
+
client.refresh!
|
377
|
+
# Update saved credentials with new access token
|
378
|
+
updated_data = {
|
379
|
+
client_id: credentials_data.client_id,
|
380
|
+
client_secret: credentials_data.client_secret,
|
381
|
+
scope: credentials_data.scope.respond_to?(:to_a) ? credentials_data.scope.to_a.join(" ") : credentials_data.scope.to_s,
|
382
|
+
refresh_token: credentials_data.refresh_token,
|
383
|
+
access_token: client.access_token,
|
384
|
+
expires_at: client.expires_at
|
385
|
+
}
|
386
|
+
Mcpeasy::Config.save_google_token(updated_data)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
client
|
391
|
+
rescue JSON::ParserError
|
392
|
+
raise "Invalid token data. Please re-run: mcpz gmail auth"
|
393
|
+
rescue => e
|
394
|
+
log_error("authorize", e)
|
395
|
+
raise "Authentication failed: #{e.message}"
|
396
|
+
end
|
397
|
+
|
398
|
+
def ensure_env!
|
399
|
+
unless Mcpeasy::Config.google_client_id && Mcpeasy::Config.google_client_secret
|
400
|
+
raise <<~ERROR
|
401
|
+
Google API credentials not configured!
|
402
|
+
Please save your Google credentials.json file using:
|
403
|
+
mcpz config set_google_credentials <path_to_credentials.json>
|
404
|
+
ERROR
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def get_email_summary(message_id)
|
409
|
+
message = @service.get_user_message("me", message_id, format: "metadata")
|
410
|
+
headers = extract_headers(message.payload)
|
411
|
+
|
412
|
+
{
|
413
|
+
id: message.id,
|
414
|
+
thread_id: message.thread_id,
|
415
|
+
subject: headers["Subject"],
|
416
|
+
from: headers["From"],
|
417
|
+
date: headers["Date"],
|
418
|
+
snippet: message.snippet,
|
419
|
+
labels: message.label_ids || []
|
420
|
+
}
|
421
|
+
rescue => e
|
422
|
+
log_error("get_email_summary", e)
|
423
|
+
nil
|
424
|
+
end
|
425
|
+
|
426
|
+
def extract_headers(payload)
|
427
|
+
headers = {}
|
428
|
+
(payload.headers || []).each do |header|
|
429
|
+
headers[header.name] = header.value
|
430
|
+
end
|
431
|
+
headers
|
432
|
+
end
|
433
|
+
|
434
|
+
def extract_body(payload)
|
435
|
+
body_data = {text: "", html: ""}
|
436
|
+
|
437
|
+
if payload.body&.data
|
438
|
+
# Simple message body
|
439
|
+
decoded_body = Base64.urlsafe_decode64(payload.body.data)
|
440
|
+
if payload.mime_type == "text/html"
|
441
|
+
body_data[:html] = decoded_body
|
442
|
+
body_data[:text] = Html2Text.convert(decoded_body)
|
443
|
+
else
|
444
|
+
body_data[:text] = decoded_body
|
445
|
+
end
|
446
|
+
elsif payload.parts
|
447
|
+
# Multipart message
|
448
|
+
extract_body_from_parts(payload.parts, body_data)
|
449
|
+
end
|
450
|
+
|
451
|
+
body_data
|
452
|
+
end
|
453
|
+
|
454
|
+
def extract_body_from_parts(parts, body_data)
|
455
|
+
parts.each do |part|
|
456
|
+
if part.mime_type == "text/plain" && part.body&.data
|
457
|
+
body_data[:text] += Base64.urlsafe_decode64(part.body.data)
|
458
|
+
elsif part.mime_type == "text/html" && part.body&.data
|
459
|
+
html_content = Base64.urlsafe_decode64(part.body.data)
|
460
|
+
body_data[:html] += html_content
|
461
|
+
# If we don't have plain text yet, convert from HTML
|
462
|
+
if body_data[:text].empty?
|
463
|
+
body_data[:text] = Html2Text.convert(html_content)
|
464
|
+
end
|
465
|
+
elsif part.parts
|
466
|
+
# Nested multipart
|
467
|
+
extract_body_from_parts(part.parts, body_data)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def extract_attachments(payload)
|
473
|
+
attachments = []
|
474
|
+
|
475
|
+
if payload.parts
|
476
|
+
extract_attachments_from_parts(payload.parts, attachments)
|
477
|
+
end
|
478
|
+
|
479
|
+
attachments
|
480
|
+
end
|
481
|
+
|
482
|
+
def extract_attachments_from_parts(parts, attachments)
|
483
|
+
parts.each do |part|
|
484
|
+
if part.filename && !part.filename.empty?
|
485
|
+
attachments << {
|
486
|
+
filename: part.filename,
|
487
|
+
mime_type: part.mime_type,
|
488
|
+
size: part.body&.size,
|
489
|
+
attachment_id: part.body&.attachment_id
|
490
|
+
}
|
491
|
+
elsif part.parts
|
492
|
+
extract_attachments_from_parts(part.parts, attachments)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
def modify_message(email_id, add_label_ids: [], remove_label_ids: [])
|
498
|
+
request = Google::Apis::GmailV1::ModifyMessageRequest.new(
|
499
|
+
add_label_ids: add_label_ids,
|
500
|
+
remove_label_ids: remove_label_ids
|
501
|
+
)
|
502
|
+
|
503
|
+
@service.modify_message("me", email_id, request)
|
504
|
+
{success: true}
|
505
|
+
rescue Google::Apis::Error => e
|
506
|
+
raise "Gmail API Error: #{e.message}"
|
507
|
+
rescue => e
|
508
|
+
log_error("modify_message", e)
|
509
|
+
raise e
|
510
|
+
end
|
511
|
+
|
512
|
+
def resolve_label_id(label)
|
513
|
+
# Handle common label names
|
514
|
+
case label.upcase
|
515
|
+
when "INBOX"
|
516
|
+
"INBOX"
|
517
|
+
when "SENT"
|
518
|
+
"SENT"
|
519
|
+
when "DRAFT"
|
520
|
+
"DRAFT"
|
521
|
+
when "SPAM"
|
522
|
+
"SPAM"
|
523
|
+
when "TRASH"
|
524
|
+
"TRASH"
|
525
|
+
when "UNREAD"
|
526
|
+
"UNREAD"
|
527
|
+
when "STARRED"
|
528
|
+
"STARRED"
|
529
|
+
when "IMPORTANT"
|
530
|
+
"IMPORTANT"
|
531
|
+
else
|
532
|
+
# For custom labels, we'd need to fetch the labels list
|
533
|
+
# For now, return the label as-is and let the API handle it
|
534
|
+
label
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
def log_error(method, error)
|
539
|
+
Mcpeasy::Config.ensure_config_dirs
|
540
|
+
File.write(
|
541
|
+
Mcpeasy::Config.log_file_path("gmail", "error"),
|
542
|
+
"#{Time.now}: #{method} error: #{error.class}: #{error.message}\n#{error.backtrace.join("\n")}\n",
|
543
|
+
mode: "a"
|
544
|
+
)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|