tempmail_sdk 1.3.3 → 1.3.5
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/README.md +1 -1
- data/lib/tempmail_sdk/channels.rb +29 -7
- data/lib/tempmail_sdk/channels_data.rb +29 -7
- data/lib/tempmail_sdk/providers/altmails.rb +99 -0
- data/lib/tempmail_sdk/providers/apihz.rb +138 -0
- data/lib/tempmail_sdk/providers/awamail.rb +95 -0
- data/lib/tempmail_sdk/providers/best_temp_mail.rb +77 -0
- data/lib/tempmail_sdk/providers/chatgpt_org_uk.rb +129 -0
- data/lib/tempmail_sdk/providers/clawdemail.rb +122 -0
- data/lib/tempmail_sdk/providers/crazymailing.rb +114 -0
- data/lib/tempmail_sdk/providers/disposablemail.rb +125 -0
- data/lib/tempmail_sdk/providers/disposablemail_app.rb +66 -0
- data/lib/tempmail_sdk/providers/email10min.rb +152 -0
- data/lib/tempmail_sdk/providers/emailnator.rb +132 -0
- data/lib/tempmail_sdk/providers/emailtemp_org.rb +131 -0
- data/lib/tempmail_sdk/providers/expressinboxhub.rb +118 -0
- data/lib/tempmail_sdk/providers/fakemail.rb +143 -0
- data/lib/tempmail_sdk/providers/firetempmail.rb +86 -0
- data/lib/tempmail_sdk/providers/flybymail.rb +93 -0
- data/lib/tempmail_sdk/providers/generator_email.rb +146 -0
- data/lib/tempmail_sdk/providers/haribu.rb +29 -3
- data/lib/tempmail_sdk/providers/huskmail.rb +132 -0
- data/lib/tempmail_sdk/providers/internxt.rb +176 -0
- data/lib/tempmail_sdk/providers/linshi_xyz.rb +79 -0
- data/lib/tempmail_sdk/providers/linshiyouxiang_net.rb +85 -0
- data/lib/tempmail_sdk/providers/mail_sunls.rb +42 -1
- data/lib/tempmail_sdk/providers/mail_td.rb +161 -0
- data/lib/tempmail_sdk/providers/mailcat_ai.rb +52 -0
- data/lib/tempmail_sdk/providers/maildrop.rb +79 -4
- data/lib/tempmail_sdk/providers/mailgolem.rb +96 -0
- data/lib/tempmail_sdk/providers/mailinator.rb +9 -2
- data/lib/tempmail_sdk/providers/mailmomy.rb +1 -1
- data/lib/tempmail_sdk/providers/mailtemp_cc.rb +91 -0
- data/lib/tempmail_sdk/providers/mailticking.rb +139 -0
- data/lib/tempmail_sdk/providers/minuteinbox.rb +174 -0
- data/lib/tempmail_sdk/providers/moakt.rb +29 -3
- data/lib/tempmail_sdk/providers/mtempmail.rb +140 -0
- data/lib/tempmail_sdk/providers/mytempmail_cc.rb +76 -0
- data/lib/tempmail_sdk/providers/nowtempmail.rb +112 -0
- data/lib/tempmail_sdk/providers/noxen_de5_net.rb +392 -0
- data/lib/tempmail_sdk/providers/nukemail.rb +188 -0
- data/lib/tempmail_sdk/providers/nullmail.rb +95 -0
- data/lib/tempmail_sdk/providers/openinbox.rb +102 -0
- data/lib/tempmail_sdk/providers/shadowmail.rb +174 -0
- data/lib/tempmail_sdk/providers/shitpost_email.rb +85 -0
- data/lib/tempmail_sdk/providers/smail_pw.rb +156 -0
- data/lib/tempmail_sdk/providers/smails.rb +128 -0
- data/lib/tempmail_sdk/providers/socketio_mail.rb +25 -0
- data/lib/tempmail_sdk/providers/temp_mail_gg.rb +362 -0
- data/lib/tempmail_sdk/providers/tempgbox.rb +118 -0
- data/lib/tempmail_sdk/providers/tempmail100.rb +148 -0
- data/lib/tempmail_sdk/providers/tempmail_cn.rb +111 -0
- data/lib/tempmail_sdk/providers/tempmail_ee.rb +373 -0
- data/lib/tempmail_sdk/providers/tempmailportal.rb +131 -0
- data/lib/tempmail_sdk/providers/tempmails_io.rb +89 -0
- data/lib/tempmail_sdk/providers/tempmailto.rb +312 -0
- data/lib/tempmail_sdk/providers/temporarymail_com.rb +184 -0
- data/lib/tempmail_sdk/providers/tenmin_app.rb +100 -0
- data/lib/tempmail_sdk/providers/tenminutemail_net.rb +183 -0
- data/lib/tempmail_sdk/providers/thirty_min_email.rb +90 -0
- data/lib/tempmail_sdk/providers/tmpkit.rb +129 -0
- data/lib/tempmail_sdk/providers/twentyfourmail_chacuo.rb +83 -0
- data/lib/tempmail_sdk/providers/vip_215.rb +171 -0
- data/lib/tempmail_sdk/providers/zerodrop.rb +90 -0
- data/lib/tempmail_sdk/registry.rb +285 -23
- data/lib/tempmail_sdk/types.rb +13 -0
- data/lib/tempmail_sdk/version.rb +1 -1
- data/lib/tempmail_sdk.rb +1 -1
- metadata +58 -6
- data/lib/tempmail_sdk/providers/gonebox_email.rb +0 -89
- data/lib/tempmail_sdk/providers/temp_mail_now.rb +0 -103
- data/lib/tempmail_sdk/providers/webmailtemp.rb +0 -108
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
module TempmailSdk
|
|
7
|
+
module Providers
|
|
8
|
+
# 24mail.chacuo.net 渠道实现
|
|
9
|
+
# POST / 同一接口用于注册和收信,HTTP only
|
|
10
|
+
module TwentyfourmailChacuo
|
|
11
|
+
CHANNEL = "24mail-chacuo"
|
|
12
|
+
BASE_URL = "http://24mail.chacuo.net"
|
|
13
|
+
DOMAINS = %w[chacuo.net 027168.com].freeze
|
|
14
|
+
|
|
15
|
+
HEADERS = {
|
|
16
|
+
"Accept" => "application/json, text/javascript, */*; q=0.01",
|
|
17
|
+
"Accept-Language" => "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
18
|
+
"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8",
|
|
19
|
+
"Origin" => BASE_URL,
|
|
20
|
+
"Referer" => "#{BASE_URL}/",
|
|
21
|
+
"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " \
|
|
22
|
+
"(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0",
|
|
23
|
+
"x-requested-with" => "XMLHttpRequest"
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
def random_local(length = 10)
|
|
29
|
+
chars = ("a".."z").to_a + ("0".."9").to_a
|
|
30
|
+
Array.new(length) { chars.sample }.join
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# 创建 24mail-chacuo 临时邮箱
|
|
34
|
+
# @return [EmailInfo]
|
|
35
|
+
def generate_email
|
|
36
|
+
name = random_local(10)
|
|
37
|
+
domain = DOMAINS.sample
|
|
38
|
+
body = "data=#{URI.encode_www_form_component("#{name}@#{domain}")}&type=refresh&arg="
|
|
39
|
+
resp = Http.post(BASE_URL + "/", headers: HEADERS, body: body, timeout: 15)
|
|
40
|
+
resp.raise_for_status
|
|
41
|
+
data = JSON.parse(resp.body)
|
|
42
|
+
raise "24mail-chacuo: 创建失败 status=#{data["status"]} info=#{data["info"]}" unless data["status"] == 1
|
|
43
|
+
|
|
44
|
+
email = "#{name}@#{domain}"
|
|
45
|
+
EmailInfo.new(channel: CHANNEL, email: email, token: email)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# 获取 24mail-chacuo 邮件列表
|
|
49
|
+
# @param email [String]
|
|
50
|
+
# @return [Array<Email>]
|
|
51
|
+
def get_emails(email)
|
|
52
|
+
addr = email.to_s.strip
|
|
53
|
+
raise "24mail-chacuo: 邮箱地址为空" if addr.empty?
|
|
54
|
+
|
|
55
|
+
at_idx = addr.index("@")
|
|
56
|
+
name = at_idx ? addr[0...at_idx] : addr
|
|
57
|
+
domain = at_idx ? addr[at_idx + 1..] : DOMAINS[0]
|
|
58
|
+
|
|
59
|
+
body = "data=#{URI.encode_www_form_component("#{name}@#{domain}")}&type=refresh&arg="
|
|
60
|
+
resp = Http.post(BASE_URL + "/", headers: HEADERS, body: body, timeout: 15)
|
|
61
|
+
resp.raise_for_status
|
|
62
|
+
data = JSON.parse(resp.body)
|
|
63
|
+
return [] unless data["status"] == 1 && data["data"].is_a?(Array) && !data["data"].empty?
|
|
64
|
+
|
|
65
|
+
list = data["data"][0]["list"]
|
|
66
|
+
return [] unless list.is_a?(Array)
|
|
67
|
+
|
|
68
|
+
list.map do |item|
|
|
69
|
+
flat = {
|
|
70
|
+
"id" => item["MID"],
|
|
71
|
+
"from" => item["FROM"],
|
|
72
|
+
"to" => addr,
|
|
73
|
+
"subject" => item["SUBJECT"],
|
|
74
|
+
"body" => item["CONTENT"],
|
|
75
|
+
"date" => item["TIME"],
|
|
76
|
+
"isRead" => false
|
|
77
|
+
}
|
|
78
|
+
Normalize.normalize_email(flat, addr)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "cgi"
|
|
6
|
+
|
|
7
|
+
module TempmailSdk
|
|
8
|
+
module Providers
|
|
9
|
+
# vip.215.im 渠道实现
|
|
10
|
+
# 流程:GET / 取首页 cookie → POST /api/temp-inbox 创建邮箱(得到 address + token)
|
|
11
|
+
# GET /v1/messages 拉取邮件列表(HTTP 优先)
|
|
12
|
+
# GET /v1/messages/{id} 补拉单封详情
|
|
13
|
+
# token 为 JWT,用于后续 API 鉴权
|
|
14
|
+
module Vip215
|
|
15
|
+
CHANNEL = "vip-215"
|
|
16
|
+
BASE = "https://vip.215.im"
|
|
17
|
+
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " \
|
|
18
|
+
"(KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0"
|
|
19
|
+
|
|
20
|
+
HOME_HEADERS = {
|
|
21
|
+
"User-Agent" => UA,
|
|
22
|
+
"Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
23
|
+
"Accept-Language" => "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
24
|
+
"Cache-Control" => "no-cache",
|
|
25
|
+
"DNT" => "1",
|
|
26
|
+
"Pragma" => "no-cache",
|
|
27
|
+
"Sec-Fetch-Dest" => "document",
|
|
28
|
+
"Sec-Fetch-Mode" => "navigate",
|
|
29
|
+
"Sec-Fetch-Site" => "same-origin",
|
|
30
|
+
"Upgrade-Insecure-Requests" => "1"
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
API_HEADERS = {
|
|
34
|
+
"User-Agent" => UA,
|
|
35
|
+
"Accept" => "*/*",
|
|
36
|
+
"Accept-Language" => "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
37
|
+
"Cache-Control" => "no-cache",
|
|
38
|
+
"Content-Type" => "application/json",
|
|
39
|
+
"DNT" => "1",
|
|
40
|
+
"Origin" => BASE,
|
|
41
|
+
"Pragma" => "no-cache",
|
|
42
|
+
"Referer" => "#{BASE}/",
|
|
43
|
+
"Sec-Fetch-Dest" => "empty",
|
|
44
|
+
"Sec-Fetch-Mode" => "cors",
|
|
45
|
+
"Sec-Fetch-Site" => "same-origin",
|
|
46
|
+
"X-Locale" => "zh"
|
|
47
|
+
}.freeze
|
|
48
|
+
|
|
49
|
+
module_function
|
|
50
|
+
|
|
51
|
+
# 合并 Set-Cookie 响应头到 cookie 字符串
|
|
52
|
+
def merge_cookies(existing, set_cookie_lines)
|
|
53
|
+
map = {}
|
|
54
|
+
existing.to_s.split(";").each do |part|
|
|
55
|
+
part = part.strip
|
|
56
|
+
next unless part.include?("=")
|
|
57
|
+
|
|
58
|
+
k, v = part.split("=", 2)
|
|
59
|
+
map[k.strip] = v.to_s.strip unless k.strip.empty?
|
|
60
|
+
end
|
|
61
|
+
set_cookie_lines.each do |line|
|
|
62
|
+
pair = line.split(";", 2).first.to_s.strip
|
|
63
|
+
next unless pair.include?("=")
|
|
64
|
+
|
|
65
|
+
k, v = pair.split("=", 2)
|
|
66
|
+
map[k.strip] = v.to_s.strip unless k.strip.empty?
|
|
67
|
+
end
|
|
68
|
+
map.map { |k, v| "#{k}=#{v}" }.join("; ")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# 访问首页获取必要 cookie(yyds_homepage_bridge / yyds_homepage_device)
|
|
72
|
+
def establish_session
|
|
73
|
+
resp = Http.get(BASE + "/", headers: HOME_HEADERS, timeout: 15)
|
|
74
|
+
resp.raise_for_status
|
|
75
|
+
cookie = merge_cookies("", resp.set_cookies)
|
|
76
|
+
raise "vip-215: 缺少首页 cookie" unless cookie.include?("yyds_homepage_bridge=") &&
|
|
77
|
+
cookie.include?("yyds_homepage_device=")
|
|
78
|
+
|
|
79
|
+
cookie
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# 创建 vip-215 临时邮箱
|
|
83
|
+
# @return [EmailInfo]
|
|
84
|
+
def generate_email
|
|
85
|
+
cookie = establish_session
|
|
86
|
+
resp = Http.post(
|
|
87
|
+
BASE + "/api/temp-inbox",
|
|
88
|
+
headers: API_HEADERS.merge("Cookie" => cookie),
|
|
89
|
+
body: "",
|
|
90
|
+
timeout: 15
|
|
91
|
+
)
|
|
92
|
+
resp.raise_for_status
|
|
93
|
+
data = JSON.parse(resp.body)
|
|
94
|
+
raise "vip-215: 创建失败" unless data["success"] && data["data"]
|
|
95
|
+
|
|
96
|
+
address = data["data"]["address"].to_s.strip
|
|
97
|
+
token = data["data"]["token"].to_s.strip
|
|
98
|
+
raise "vip-215: 响应缺少 address 或 token" if address.empty? || token.empty?
|
|
99
|
+
|
|
100
|
+
EmailInfo.new(channel: CHANNEL, email: address, token: token)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# 判断邮件条目是否已含真实正文
|
|
104
|
+
def has_real_body?(row)
|
|
105
|
+
%w[text body_text html body_html content body].any? do |key|
|
|
106
|
+
v = row[key]
|
|
107
|
+
v.is_a?(String) && !v.strip.empty?
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# 提取邮件 ID
|
|
112
|
+
def extract_message_id(row)
|
|
113
|
+
%w[id messageId message_id].each do |key|
|
|
114
|
+
v = row[key]
|
|
115
|
+
next if v.nil?
|
|
116
|
+
return v.to_s.strip if v.is_a?(String) && !v.strip.empty?
|
|
117
|
+
return v.to_i.to_s if v.is_a?(Numeric)
|
|
118
|
+
end
|
|
119
|
+
""
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# 补拉单封邮件详情
|
|
123
|
+
def fetch_message_detail(token, id)
|
|
124
|
+
mid = id.to_s.strip
|
|
125
|
+
return nil if mid.empty?
|
|
126
|
+
|
|
127
|
+
resp = Http.get(
|
|
128
|
+
"#{BASE}/v1/messages/#{URI.encode_www_form_component(mid)}",
|
|
129
|
+
headers: API_HEADERS.merge("Authorization" => "Bearer #{token}"),
|
|
130
|
+
timeout: 15
|
|
131
|
+
)
|
|
132
|
+
return nil unless resp.status_code == 200
|
|
133
|
+
|
|
134
|
+
parsed = JSON.parse(resp.body)
|
|
135
|
+
return nil unless parsed["success"] && parsed["data"].is_a?(Hash)
|
|
136
|
+
|
|
137
|
+
parsed["data"]
|
|
138
|
+
rescue StandardError
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# 获取 vip-215 邮件列表(HTTP GET /v1/messages)
|
|
143
|
+
# @param token [String] JWT
|
|
144
|
+
# @param email [String]
|
|
145
|
+
# @return [Array<Email>]
|
|
146
|
+
def get_emails(token, email)
|
|
147
|
+
raise "vip-215: token 为空" if token.to_s.strip.empty?
|
|
148
|
+
|
|
149
|
+
resp = Http.get(
|
|
150
|
+
BASE + "/v1/messages",
|
|
151
|
+
headers: API_HEADERS.merge("Authorization" => "Bearer #{token}"),
|
|
152
|
+
timeout: 15
|
|
153
|
+
)
|
|
154
|
+
resp.raise_for_status
|
|
155
|
+
parsed = JSON.parse(resp.body)
|
|
156
|
+
raise "vip-215: messages success=false" unless parsed["success"]
|
|
157
|
+
|
|
158
|
+
messages = parsed.dig("data", "messages") || []
|
|
159
|
+
messages.map do |row|
|
|
160
|
+
id = extract_message_id(row)
|
|
161
|
+
unless has_real_body?(row) || id.empty?
|
|
162
|
+
detail = fetch_message_detail(token, id)
|
|
163
|
+
row = row.merge(detail) if detail
|
|
164
|
+
end
|
|
165
|
+
row["to"] ||= email
|
|
166
|
+
Normalize.normalize_email(row, email)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module TempmailSdk
|
|
6
|
+
module Providers
|
|
7
|
+
# Zerodrop 渠道实现(zerodrop.dev)
|
|
8
|
+
#
|
|
9
|
+
# 无认证 REST:建箱无需请求,本地生成 "sdk"+8 位随机名,地址为 <名>@zerodrop-sandbox.online;
|
|
10
|
+
# 读信 GET /api/inbox/{name}?source=sdk,响应形如 {"emails":[...],"count":N}。
|
|
11
|
+
# 平台邮件对象只有 id/from/to/subject/receivedAt/raw/otp/magicLink,正文仅存在于
|
|
12
|
+
# raw(完整 MIME 原文),需剥离头部提取纯文本 body。
|
|
13
|
+
module Zerodrop
|
|
14
|
+
CHANNEL = "zerodrop"
|
|
15
|
+
BASE_URL = "https://zerodrop.dev"
|
|
16
|
+
DOMAIN = "zerodrop-sandbox.online"
|
|
17
|
+
|
|
18
|
+
HEADERS = {
|
|
19
|
+
"Accept" => "application/json",
|
|
20
|
+
"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " \
|
|
21
|
+
"(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0"
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
# 生成 "sdk"+8 位 [a-z0-9] 随机本地名
|
|
27
|
+
CHARS = ("a".."z").to_a + ("0".."9").to_a
|
|
28
|
+
def random_name
|
|
29
|
+
"sdk" + Array.new(8) { CHARS.sample }.join
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# 从 raw(完整 MIME 原文)提取纯文本正文:
|
|
33
|
+
# 定位首个空行(RFC 5322 头部/正文分隔,\r\n\r\n 或 \n\n),其后部分即 body
|
|
34
|
+
# @param raw [String] MIME 原文
|
|
35
|
+
# @return [String]
|
|
36
|
+
def raw_body(raw)
|
|
37
|
+
idx = raw.index("\r\n\r\n")
|
|
38
|
+
return raw[(idx + 4)..] if idx
|
|
39
|
+
|
|
40
|
+
idx = raw.index("\n\n")
|
|
41
|
+
return raw[(idx + 2)..] if idx
|
|
42
|
+
|
|
43
|
+
""
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# 创建临时邮箱
|
|
47
|
+
# 建箱无需请求,本地生成随机名,token 复用完整地址以便收件箱回查
|
|
48
|
+
# @return [EmailInfo]
|
|
49
|
+
def generate_email
|
|
50
|
+
email = "#{random_name}@#{DOMAIN}"
|
|
51
|
+
EmailInfo.new(channel: CHANNEL, email: email, token: email)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# 读取收件箱
|
|
55
|
+
# GET /api/inbox/{name}?source=sdk
|
|
56
|
+
# @param email [String] 邮箱地址
|
|
57
|
+
# @param _token [String] 令牌(本渠道忽略,以邮箱为准)
|
|
58
|
+
# @return [Array<Email>]
|
|
59
|
+
def get_emails(email, _token)
|
|
60
|
+
addr = email.to_s.strip
|
|
61
|
+
parts = addr.split("@", 2)
|
|
62
|
+
raise "zerodrop: 非 #{DOMAIN} 域邮箱地址" unless parts.size == 2 && parts[1] == DOMAIN
|
|
63
|
+
|
|
64
|
+
url = "#{BASE_URL}/api/inbox/#{URI.encode_www_form_component(parts[0])}?source=sdk"
|
|
65
|
+
resp = Http.get(url, headers: HEADERS, timeout: 15)
|
|
66
|
+
resp.raise_for_status
|
|
67
|
+
data = resp.json
|
|
68
|
+
emails = data.is_a?(Hash) ? data["emails"] : nil
|
|
69
|
+
return [] unless emails.is_a?(Array)
|
|
70
|
+
|
|
71
|
+
emails.filter_map do |m|
|
|
72
|
+
next unless m.is_a?(Hash)
|
|
73
|
+
|
|
74
|
+
row = {
|
|
75
|
+
"id" => m["id"],
|
|
76
|
+
"from" => m["from"],
|
|
77
|
+
"to" => addr,
|
|
78
|
+
"subject" => m["subject"],
|
|
79
|
+
"date" => m["receivedAt"] || m["date"]
|
|
80
|
+
}
|
|
81
|
+
# 正文仅存在于 raw(完整 MIME 原文):提取纯文本 body 作 text
|
|
82
|
+
raw = m["raw"].to_s
|
|
83
|
+
body = raw_body(raw)
|
|
84
|
+
row["text"] = body unless body.empty?
|
|
85
|
+
Normalize.normalize_email(row, addr)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|