tempmail_sdk 1.3.4 → 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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/tempmail_sdk/channels.rb +29 -7
  4. data/lib/tempmail_sdk/channels_data.rb +29 -7
  5. data/lib/tempmail_sdk/providers/clawdemail.rb +122 -0
  6. data/lib/tempmail_sdk/providers/crazymailing.rb +114 -0
  7. data/lib/tempmail_sdk/providers/firetempmail.rb +86 -0
  8. data/lib/tempmail_sdk/providers/flybymail.rb +93 -0
  9. data/lib/tempmail_sdk/providers/generator_email.rb +146 -0
  10. data/lib/tempmail_sdk/providers/huskmail.rb +132 -0
  11. data/lib/tempmail_sdk/providers/internxt.rb +176 -0
  12. data/lib/tempmail_sdk/providers/linshi_xyz.rb +79 -0
  13. data/lib/tempmail_sdk/providers/mailmomy.rb +1 -1
  14. data/lib/tempmail_sdk/providers/mailticking.rb +139 -0
  15. data/lib/tempmail_sdk/providers/mtempmail.rb +140 -0
  16. data/lib/tempmail_sdk/providers/nowtempmail.rb +112 -0
  17. data/lib/tempmail_sdk/providers/noxen_de5_net.rb +392 -0
  18. data/lib/tempmail_sdk/providers/nukemail.rb +188 -0
  19. data/lib/tempmail_sdk/providers/nullmail.rb +95 -0
  20. data/lib/tempmail_sdk/providers/shadowmail.rb +174 -0
  21. data/lib/tempmail_sdk/providers/shitpost_email.rb +85 -0
  22. data/lib/tempmail_sdk/providers/smails.rb +128 -0
  23. data/lib/tempmail_sdk/providers/temp_mail_gg.rb +362 -0
  24. data/lib/tempmail_sdk/providers/tempmail100.rb +148 -0
  25. data/lib/tempmail_sdk/providers/tempmail_ee.rb +373 -0
  26. data/lib/tempmail_sdk/providers/tempmailportal.rb +131 -0
  27. data/lib/tempmail_sdk/providers/tempmails_io.rb +89 -0
  28. data/lib/tempmail_sdk/providers/tempmailto.rb +312 -0
  29. data/lib/tempmail_sdk/providers/temporarymail_com.rb +184 -0
  30. data/lib/tempmail_sdk/providers/tenmin_app.rb +100 -0
  31. data/lib/tempmail_sdk/providers/thirty_min_email.rb +90 -0
  32. data/lib/tempmail_sdk/providers/tmpkit.rb +129 -0
  33. data/lib/tempmail_sdk/providers/zerodrop.rb +90 -0
  34. data/lib/tempmail_sdk/registry.rb +129 -29
  35. data/lib/tempmail_sdk/types.rb +13 -0
  36. data/lib/tempmail_sdk/version.rb +1 -1
  37. data/lib/tempmail_sdk.rb +1 -1
  38. metadata +31 -7
  39. data/lib/tempmail_sdk/providers/gonebox_email.rb +0 -89
  40. data/lib/tempmail_sdk/providers/mohmal.rb +0 -208
  41. data/lib/tempmail_sdk/providers/temp_mail_now.rb +0 -103
  42. data/lib/tempmail_sdk/providers/webmailtemp.rb +0 -108
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TempmailSdk
4
- module Providers
5
- # gonebox.email 渠道实现
6
- # 一次性临时邮箱服务,无需认证
7
- module GoneboxEmail
8
- CHANNEL = "gonebox-email"
9
- BASE_URL = "https://api.gonebox.email/api/v1"
10
- HEADERS = {
11
- "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " \
12
- "(KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36",
13
- "Accept" => "application/json",
14
- "Content-Type" => "application/json"
15
- }.freeze
16
-
17
- module_function
18
-
19
- # @return [EmailInfo]
20
- def generate_email
21
- resp = Http.post(
22
- "#{BASE_URL}/inboxes",
23
- headers: HEADERS,
24
- json: { "domain" => "gonebox.email" },
25
- timeout: 15
26
- )
27
- resp.raise_for_status
28
- body = resp.json
29
- raise "gonebox-email: create failed" unless body.is_a?(Hash) && body["success"]
30
-
31
- data = body["data"]
32
- raise "gonebox-email: invalid data" unless data.is_a?(Hash)
33
-
34
- address = data["address"].to_s
35
- raise "gonebox-email: missing address" if address.empty?
36
-
37
- # expiresAt 为秒级时间戳,转为毫秒
38
- expires_at = nil
39
- raw_expires = data["expiresAt"]
40
- if raw_expires
41
- begin
42
- expires_at = raw_expires.to_i * 1000
43
- rescue StandardError
44
- # 忽略
45
- end
46
- end
47
-
48
- EmailInfo.new(channel: CHANNEL, email: address, token: "", expires_at: expires_at)
49
- end
50
-
51
- # @param token [String]
52
- # @param email [String]
53
- # @return [Array<Email>]
54
- def get_emails(token, email)
55
- address = email.to_s.strip
56
- raise "gonebox-email: missing email" if address.empty?
57
-
58
- headers = HEADERS.reject { |k, _| k == "Content-Type" }
59
- resp = Http.get("#{BASE_URL}/inboxes/#{address}/messages", headers: headers, timeout: 15)
60
- return [] if resp.status_code == 404
61
-
62
- resp.raise_for_status
63
- body = resp.json
64
- return [] unless body.is_a?(Hash) && body["success"]
65
-
66
- data = body["data"]
67
- return [] unless data.is_a?(Hash)
68
-
69
- messages = data["messages"]
70
- return [] unless messages.is_a?(Array) && !messages.empty?
71
-
72
- messages.select { |m| m.is_a?(Hash) }.map do |msg|
73
- raw = {
74
- "id" => msg["id"].to_s,
75
- "from" => (msg["from"] || msg["sender"]).to_s,
76
- "to" => address,
77
- "subject" => msg["subject"].to_s,
78
- "text" => (msg["text"] || msg["body_plain"]).to_s,
79
- "html" => (msg["html"] || msg["body_html"]).to_s,
80
- "date" => (msg["date"] || msg["received_at"]).to_s,
81
- "is_read" => false,
82
- "attachments" => []
83
- }
84
- Normalize.normalize_email(raw, address)
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,208 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "cgi"
4
-
5
- module TempmailSdk
6
- module Providers
7
- # mohmal.com 渠道实现(HTML 页面解析,session cookie)
8
- #
9
- # 流程:GET /en/create/random 创建邮箱(跟随重定向到 /en/inbox),从 data-email 属性提取邮箱地址
10
- # GET /en/inbox 解析邮件链接列表
11
- # GET /en/message/{id}/html 获取邮件详情
12
- # token 存储 connect.sid cookie 字符串
13
- # 注意:正文提取使用栈式深度匹配,避免非贪婪正则截断嵌套 div
14
- module Mohmal
15
- CHANNEL = "mohmal"
16
- BASE_URL = "https://www.mohmal.com"
17
-
18
- DATA_EMAIL_RE = /data-email=["']([^"']+)["']/im
19
- MSG_HREF_RE = /href=["'](\/en\/message\/([^"']+))["']/i
20
- DETAIL_FROM_RE = /<span[^>]+class=["'][^"']*from[^"']*["'][^>]*>([\s\S]*?)<\/span>/im
21
- DETAIL_SUBJECT_RE = /<span[^>]+class=["'][^"']*subject[^"']*["'][^>]*>([\s\S]*?)<\/span>/im
22
- DETAIL_DATE_RE = /<span[^>]+class=["'][^"']*date[^"']*["'][^>]*>([\s\S]*?)<\/span>/im
23
- TAG_RE = /<[^>]+>/
24
- BODY_OPEN_RE = /<div\s+class=["'][^"']*(?:mail-body|message-body|message_body)[^"']*["']\s*>/im
25
-
26
- BROWSER_HEADERS = {
27
- "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " \
28
- "(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0",
29
- "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif," \
30
- "image/webp,image/apng,*/*;q=0.8",
31
- "Accept-Language" => "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
32
- "Cache-Control" => "no-cache",
33
- "DNT" => "1",
34
- "Pragma" => "no-cache",
35
- "Upgrade-Insecure-Requests" => "1"
36
- }.freeze
37
-
38
- module_function
39
-
40
- # 从 HTML 中提取 data-email 属性值
41
- # @param html [String]
42
- # @return [String]
43
- def extract_email(html)
44
- m = html.match(DATA_EMAIL_RE)
45
- m ? CGI.unescapeHTML(m[1].strip) : ""
46
- end
47
-
48
- # 合并 Cookie 头与响应 Set-Cookie
49
- # @param existing [String]
50
- # @param set_cookie_lines [Array<String>]
51
- # @return [String]
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
- # 去除 HTML 标签并修剪空白
72
- # @param s [String]
73
- # @return [String]
74
- def strip_tags(s)
75
- s.gsub(TAG_RE, " ").strip
76
- end
77
-
78
- # 使用栈式深度匹配提取指定 class 的 div 完整内部 HTML
79
- # 避免非贪婪正则在嵌套 div 时截断正文
80
- # @param page [String]
81
- # @return [String]
82
- def extract_body_html(page)
83
- m = page.match(BODY_OPEN_RE)
84
- return "" unless m
85
-
86
- start = m.end(0)
87
- pos = start
88
- depth = 1
89
- while pos < page.length && depth > 0
90
- next_open = page.index("<div", pos)
91
- next_close = page.index("</div>", pos)
92
- break unless next_close
93
-
94
- if next_open && next_open < next_close
95
- depth += 1
96
- pos = next_open + 4
97
- else
98
- depth -= 1
99
- return page[start...next_close].strip if depth.zero?
100
-
101
- pos = next_close + 6
102
- end
103
- end
104
- ""
105
- end
106
-
107
- # 创建 mohmal.com 临时邮箱
108
- # @return [EmailInfo]
109
- def generate_email
110
- r1 = Http.get(
111
- BASE_URL + "/en/create/random",
112
- headers: BROWSER_HEADERS.merge("Referer" => BASE_URL + "/en"),
113
- timeout: 15
114
- )
115
- r1.raise_for_status
116
- cookie_hdr = merge_cookies("", r1.set_cookies)
117
- email = extract_email(r1.body)
118
-
119
- if email.empty?
120
- r2 = Http.get(
121
- BASE_URL + "/en/inbox",
122
- headers: BROWSER_HEADERS.merge("Referer" => BASE_URL + "/en", "Cookie" => cookie_hdr),
123
- timeout: 15
124
- )
125
- r2.raise_for_status
126
- cookie_hdr = merge_cookies(cookie_hdr, r2.set_cookies)
127
- email = extract_email(r2.body)
128
- end
129
-
130
- raise "mohmal: 未能从页面中提取邮箱地址" if email.empty?
131
-
132
- EmailInfo.new(channel: CHANNEL, email: email, token: cookie_hdr)
133
- end
134
-
135
- # 获取 mohmal.com 邮件列表
136
- # @param email [String]
137
- # @param token [String] connect.sid cookie 字符串
138
- # @return [Array<Email>]
139
- def get_emails(email, token)
140
- raise "mohmal: session cookie 为空" if token.to_s.strip.empty?
141
-
142
- r = Http.get(
143
- BASE_URL + "/en/inbox",
144
- headers: BROWSER_HEADERS.merge(
145
- "Referer" => BASE_URL + "/en",
146
- "Cookie" => token
147
- ),
148
- timeout: 15
149
- )
150
- r.raise_for_status
151
-
152
- seen = {}
153
- msgs = []
154
- r.body.scan(MSG_HREF_RE) do |path, id|
155
- next if path.include?("/delete")
156
- next if seen[id]
157
-
158
- seen[id] = true
159
- msgs << { path: path, id: id }
160
- end
161
- return [] if msgs.empty?
162
-
163
- msgs.filter_map do |msg|
164
- raw = fetch_detail(token, msg[:id], email)
165
- Normalize.normalize_email(raw, email)
166
- end
167
- end
168
-
169
- # 获取单封邮件详情页
170
- # @param cookie [String]
171
- # @param id [String]
172
- # @param recipient [String]
173
- # @return [Hash]
174
- def fetch_detail(cookie, id, recipient)
175
- raw = { "id" => id, "to" => recipient }
176
- detail_url = "#{BASE_URL}/en/message/#{id}/html"
177
- resp = Http.get(
178
- detail_url,
179
- headers: BROWSER_HEADERS.merge(
180
- "Referer" => "#{BASE_URL}/en/inbox",
181
- "Cookie" => cookie
182
- ),
183
- timeout: 15
184
- )
185
- return raw unless resp.status_code == 200
186
-
187
- page = resp.body
188
-
189
- if (m = page.match(DETAIL_FROM_RE))
190
- raw["from"] = strip_tags(CGI.unescapeHTML(m[1])).strip
191
- end
192
- if (m = page.match(DETAIL_SUBJECT_RE))
193
- raw["subject"] = CGI.unescapeHTML(strip_tags(m[1])).strip
194
- end
195
- if (m = page.match(DETAIL_DATE_RE))
196
- raw["date"] = CGI.unescapeHTML(strip_tags(m[1])).strip
197
- end
198
-
199
- body_html = extract_body_html(page)
200
- raw["html"] = body_html unless body_html.empty?
201
-
202
- raw
203
- rescue StandardError
204
- raw
205
- end
206
- end
207
- end
208
- end
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TempmailSdk
4
- module Providers
5
- # Temp-Mail.Now 渠道 — https://temp-mail.now
6
- # 基于会话 Cookie 的临时邮箱服务
7
- module TempMailNow
8
- CHANNEL = "temp-mail-now"
9
- BASE_URL = "https://temp-mail.now"
10
-
11
- PAGE_HEADERS = {
12
- "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
13
- "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
14
- }.freeze
15
-
16
- API_HEADERS = {
17
- "Accept" => "application/json, text/javascript, */*; q=0.01",
18
- "X-Requested-With" => "XMLHttpRequest",
19
- "Referer" => "#{BASE_URL}/en/",
20
- "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
21
- }.freeze
22
-
23
- module_function
24
-
25
- def extract_cookies(resp)
26
- cookies = {}
27
- resp.set_cookies.each do |line|
28
- pair = line.split(";").first.to_s.strip
29
- k, v = pair.split("=", 2)
30
- cookies[k] = v if k && v
31
- end
32
- cookies
33
- end
34
-
35
- def cookies_to_str(cookies)
36
- cookies.map { |k, v| "#{k}=#{v}" }.join("; ")
37
- end
38
-
39
- # 创建临时邮箱
40
- # @return [EmailInfo]
41
- def generate_email
42
- # 获取 session cookie
43
- resp = Http.get("#{BASE_URL}/en/", headers: PAGE_HEADERS, timeout: 15)
44
- resp.raise_for_status
45
- cookies = extract_cookies(resp)
46
- cookie_str = cookies_to_str(cookies)
47
- raise "temp-mail-now: 无法获取 session cookie" if cookie_str.empty?
48
-
49
- # 创建新邮箱
50
- resp2 = Http.post("#{BASE_URL}/change_email",
51
- headers: API_HEADERS.merge("Cookie" => cookie_str), timeout: 15)
52
- resp2.raise_for_status
53
-
54
- # 合并可能更新的 cookie
55
- extract_cookies(resp2).each { |k, v| cookies[k] = v }
56
- cookie_str = cookies_to_str(cookies)
57
-
58
- data = resp2.json
59
- raise "temp-mail-now: 响应格式无效" unless data.is_a?(Hash)
60
-
61
- address = data["new_email"] || ""
62
- raise "temp-mail-now: 创建邮箱失败" if address.empty? || !address.include?("@")
63
-
64
- EmailInfo.new(channel: CHANNEL, email: address, token: cookie_str)
65
- end
66
-
67
- # 获取邮件列表
68
- # @param token [String] session cookie
69
- # @param email [String]
70
- # @return [Array<Email>]
71
- def get_emails(token, email)
72
- raise "temp-mail-now: session cookie 不能为空" if token.nil? || token.empty?
73
-
74
- addr = (email || "").strip
75
- raise "temp-mail-now: 邮箱地址不能为空" if addr.empty?
76
-
77
- resp = Http.get("#{BASE_URL}/fetch_emails",
78
- headers: API_HEADERS.merge("Cookie" => token), timeout: 15)
79
- resp.raise_for_status
80
- data = resp.json
81
- return [] unless data.is_a?(Hash)
82
-
83
- emails_list = data["emails"]
84
- return [] unless emails_list.is_a?(Array)
85
-
86
- emails_list.filter_map do |item|
87
- next unless item.is_a?(Hash)
88
-
89
- raw = {
90
- "id" => item["id"] || "",
91
- "from" => item["from"] || item["from_address"] || item["sender"] || "",
92
- "to" => item["to"] || addr,
93
- "subject" => item["subject"] || "",
94
- "text" => item["text"] || item["body_text"] || "",
95
- "html" => item["html"] || item["body_html"] || "",
96
- "date" => item["date"] || item["received_at"] || ""
97
- }
98
- Normalize.normalize_email(raw, addr)
99
- end
100
- end
101
- end
102
- end
103
- end
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "base64"
4
- require "json"
5
- require "uri"
6
-
7
- module TempmailSdk
8
- module Providers
9
- # webmailtemp.com 临时邮箱渠道
10
- # GET /api/create 创建邮箱
11
- # GET /api/check/{username} 获取邮件列表
12
- module Webmailtemp
13
- CHANNEL = "webmailtemp"
14
- BASE_URL = "https://webmailtemp.com"
15
- TOK_PREFIX = "wmt1:"
16
-
17
- HEADERS = {
18
- "Accept" => "application/json",
19
- "User-Agent" => "Mozilla/5.0"
20
- }.freeze
21
-
22
- module_function
23
-
24
- def encode_token(username, cookie_hdr)
25
- raw = JSON.generate({ "u" => username, "c" => cookie_hdr })
26
- "#{TOK_PREFIX}#{Base64.strict_encode64(raw)}"
27
- end
28
-
29
- def decode_token(token)
30
- raise "webmailtemp: 无效的 token" unless token&.start_with?(TOK_PREFIX)
31
-
32
- data = JSON.parse(Base64.decode64(token[TOK_PREFIX.length..]))
33
- username = (data["u"] || "").strip
34
- cookie_hdr = (data["c"] || "").strip
35
- raise "webmailtemp: token 数据无效" if username.empty? || cookie_hdr.empty?
36
-
37
- { "username" => username, "cookie" => cookie_hdr }
38
- end
39
-
40
- # @return [EmailInfo]
41
- def generate_email
42
- resp = Http.get("#{BASE_URL}/api/create", headers: HEADERS, timeout: 15)
43
- resp.raise_for_status
44
- data = resp.json
45
- raise "webmailtemp: 创建邮箱响应无效" unless data.is_a?(Hash)
46
-
47
- email = (data["email"] || "").strip
48
- username = (data["username"] || "").strip
49
-
50
- # 从 set_cookies 提取第一个 cookie
51
- cookie_hdr = ""
52
- cookies = resp.set_cookies
53
- unless cookies.empty?
54
- pair = cookies.first.split(";").first.to_s.strip
55
- cookie_hdr = pair unless pair.empty?
56
- end
57
-
58
- unless data["success"] && !email.empty? && email.include?("@") && !username.empty? && !cookie_hdr.empty?
59
- raise "webmailtemp: 创建邮箱响应无效"
60
- end
61
-
62
- expires_at = nil
63
- ttl = data["ttl"]
64
- if ttl.is_a?(Numeric) && ttl > 0
65
- expires_at = ((Time.now.to_f + ttl) * 1000).to_i
66
- end
67
-
68
- EmailInfo.new(channel: CHANNEL, email: email, token: encode_token(username, cookie_hdr),
69
- expires_at: expires_at)
70
- end
71
-
72
- # @param token [String]
73
- # @param email [String]
74
- # @return [Array<Email>]
75
- def get_emails(token, email)
76
- address = (email || "").strip
77
- raise "webmailtemp: 邮箱地址为空" if address.empty?
78
-
79
- session = decode_token((token || "").strip)
80
- headers = HEADERS.merge("Cookie" => session["cookie"])
81
-
82
- resp = Http.get("#{BASE_URL}/api/check/#{URI.encode_www_form_component(session["username"])}",
83
- headers: headers, timeout: 15)
84
- resp.raise_for_status
85
- data = resp.json
86
-
87
- rows = data.is_a?(Hash) ? data["emails"] : nil
88
- return [] unless rows.is_a?(Array)
89
-
90
- rows.filter_map do |row|
91
- next unless row.is_a?(Hash)
92
-
93
- raw = {
94
- "id" => row["id"],
95
- "from" => row["from"],
96
- "to" => address,
97
- "subject" => row["subject"],
98
- "text" => row["body"],
99
- "html" => row["html"],
100
- "date" => row["received_at"] || row["timestamp"],
101
- "attachments" => row["attachments"]
102
- }
103
- Normalize.normalize_email(raw, address)
104
- end
105
- end
106
- end
107
- end
108
- end