tempmail_sdk 1.3.3

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +116 -0
  3. data/lib/tempmail_sdk/channels.rb +289 -0
  4. data/lib/tempmail_sdk/channels_data.rb +289 -0
  5. data/lib/tempmail_sdk/client.rb +193 -0
  6. data/lib/tempmail_sdk/config.rb +104 -0
  7. data/lib/tempmail_sdk/html_utils.rb +42 -0
  8. data/lib/tempmail_sdk/http.rb +225 -0
  9. data/lib/tempmail_sdk/logger.rb +38 -0
  10. data/lib/tempmail_sdk/normalize.rb +193 -0
  11. data/lib/tempmail_sdk/providers/anonbox.rb +248 -0
  12. data/lib/tempmail_sdk/providers/anonymmail.rb +92 -0
  13. data/lib/tempmail_sdk/providers/byom.rb +48 -0
  14. data/lib/tempmail_sdk/providers/catchmail.rb +110 -0
  15. data/lib/tempmail_sdk/providers/cleantempmail.rb +49 -0
  16. data/lib/tempmail_sdk/providers/devmail_uk.rb +58 -0
  17. data/lib/tempmail_sdk/providers/dropmail.rb +133 -0
  18. data/lib/tempmail_sdk/providers/dropmail_click.rb +81 -0
  19. data/lib/tempmail_sdk/providers/dropmail_me.rb +163 -0
  20. data/lib/tempmail_sdk/providers/duckmail.rb +63 -0
  21. data/lib/tempmail_sdk/providers/eyepaste.rb +119 -0
  22. data/lib/tempmail_sdk/providers/fake_email_site.rb +48 -0
  23. data/lib/tempmail_sdk/providers/fake_legal.rb +109 -0
  24. data/lib/tempmail_sdk/providers/fmail.rb +87 -0
  25. data/lib/tempmail_sdk/providers/freecustom.rb +135 -0
  26. data/lib/tempmail_sdk/providers/getnada.rb +146 -0
  27. data/lib/tempmail_sdk/providers/gonebox_email.rb +89 -0
  28. data/lib/tempmail_sdk/providers/guerrillamail.rb +62 -0
  29. data/lib/tempmail_sdk/providers/guerrillamail_mirrors.rb +81 -0
  30. data/lib/tempmail_sdk/providers/harakirimail.rb +84 -0
  31. data/lib/tempmail_sdk/providers/haribu.rb +176 -0
  32. data/lib/tempmail_sdk/providers/inboxes.rb +110 -0
  33. data/lib/tempmail_sdk/providers/inboxkitten.rb +90 -0
  34. data/lib/tempmail_sdk/providers/linshiyou.rb +157 -0
  35. data/lib/tempmail_sdk/providers/lroid.rb +170 -0
  36. data/lib/tempmail_sdk/providers/m2u.rb +126 -0
  37. data/lib/tempmail_sdk/providers/mail10s.rb +59 -0
  38. data/lib/tempmail_sdk/providers/mail123.rb +82 -0
  39. data/lib/tempmail_sdk/providers/mail_cx.rb +133 -0
  40. data/lib/tempmail_sdk/providers/mail_sunls.rb +68 -0
  41. data/lib/tempmail_sdk/providers/mail_tm.rb +99 -0
  42. data/lib/tempmail_sdk/providers/mailcatch.rb +91 -0
  43. data/lib/tempmail_sdk/providers/maildrop.rb +92 -0
  44. data/lib/tempmail_sdk/providers/maildrop_cc.rb +110 -0
  45. data/lib/tempmail_sdk/providers/mailforspam.rb +96 -0
  46. data/lib/tempmail_sdk/providers/mailhole_de.rb +70 -0
  47. data/lib/tempmail_sdk/providers/mailinator.rb +182 -0
  48. data/lib/tempmail_sdk/providers/mailmomy.rb +108 -0
  49. data/lib/tempmail_sdk/providers/mailnesia.rb +141 -0
  50. data/lib/tempmail_sdk/providers/mffac.rb +101 -0
  51. data/lib/tempmail_sdk/providers/moakt.rb +296 -0
  52. data/lib/tempmail_sdk/providers/neighbours.rb +140 -0
  53. data/lib/tempmail_sdk/providers/neighbours_sh.rb +117 -0
  54. data/lib/tempmail_sdk/providers/nimail.rb +100 -0
  55. data/lib/tempmail_sdk/providers/ockito.rb +163 -0
  56. data/lib/tempmail_sdk/providers/one_sec_mail.rb +46 -0
  57. data/lib/tempmail_sdk/providers/restmail_net.rb +60 -0
  58. data/lib/tempmail_sdk/providers/rootsh.rb +167 -0
  59. data/lib/tempmail_sdk/providers/shitty_email.rb +84 -0
  60. data/lib/tempmail_sdk/providers/smailpro.rb +103 -0
  61. data/lib/tempmail_sdk/providers/ta_easy.rb +53 -0
  62. data/lib/tempmail_sdk/providers/temp_mail_io.rb +67 -0
  63. data/lib/tempmail_sdk/providers/temp_mail_now.rb +103 -0
  64. data/lib/tempmail_sdk/providers/temp_mail_org.rb +89 -0
  65. data/lib/tempmail_sdk/providers/tempemail_co.rb +142 -0
  66. data/lib/tempmail_sdk/providers/tempemail_info.rb +121 -0
  67. data/lib/tempmail_sdk/providers/tempemails_net.rb +159 -0
  68. data/lib/tempmail_sdk/providers/tempfastmail.rb +76 -0
  69. data/lib/tempmail_sdk/providers/tempgmailer.rb +126 -0
  70. data/lib/tempmail_sdk/providers/tempgo_email.rb +74 -0
  71. data/lib/tempmail_sdk/providers/tempinbox.rb +70 -0
  72. data/lib/tempmail_sdk/providers/tempmail.rb +66 -0
  73. data/lib/tempmail_sdk/providers/tempmail365.rb +119 -0
  74. data/lib/tempmail_sdk/providers/tempmail_fish.rb +89 -0
  75. data/lib/tempmail_sdk/providers/tempmail_fyi.rb +124 -0
  76. data/lib/tempmail_sdk/providers/tempmail_lol.rb +42 -0
  77. data/lib/tempmail_sdk/providers/tempmail_lol_v2.rb +57 -0
  78. data/lib/tempmail_sdk/providers/tempmail_plus.rb +80 -0
  79. data/lib/tempmail_sdk/providers/tempmailc.rb +80 -0
  80. data/lib/tempmail_sdk/providers/tempmailpro.rb +89 -0
  81. data/lib/tempmail_sdk/providers/tempmailten.rb +150 -0
  82. data/lib/tempmail_sdk/providers/temporam.rb +95 -0
  83. data/lib/tempmail_sdk/providers/tempp_mails.rb +150 -0
  84. data/lib/tempmail_sdk/providers/tempy_email.rb +73 -0
  85. data/lib/tempmail_sdk/providers/ten_minute_mail_net.rb +125 -0
  86. data/lib/tempmail_sdk/providers/tenminute_one.rb +255 -0
  87. data/lib/tempmail_sdk/providers/throwawaymail.rb +80 -0
  88. data/lib/tempmail_sdk/providers/tmail_link.rb +98 -0
  89. data/lib/tempmail_sdk/providers/uncorreotemporal.rb +84 -0
  90. data/lib/tempmail_sdk/providers/webmailtemp.rb +108 -0
  91. data/lib/tempmail_sdk/providers/xkx_me.rb +125 -0
  92. data/lib/tempmail_sdk/providers/zhujump.rb +256 -0
  93. data/lib/tempmail_sdk/registry.rb +1097 -0
  94. data/lib/tempmail_sdk/retry.rb +91 -0
  95. data/lib/tempmail_sdk/telemetry.rb +134 -0
  96. data/lib/tempmail_sdk/types.rb +164 -0
  97. data/lib/tempmail_sdk/version.rb +6 -0
  98. data/lib/tempmail_sdk/webui.rb +360 -0
  99. data/lib/tempmail_sdk.rb +76 -0
  100. metadata +142 -0
@@ -0,0 +1,256 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "json"
5
+ require "securerandom"
6
+ require "base64"
7
+ require "net/http"
8
+
9
+ module TempmailSdk
10
+ module Providers
11
+ # mail.zhujump.com 固定域渠道
12
+ # 通过注册账号、登录会话后创建固定域邮箱,再通过邮箱 ID 拉取邮件列表
13
+ module Zhujump
14
+ BASE_URL = "https://mail.zhujump.com"
15
+ TOKEN_PREFIX = "zhj1:"
16
+ DEFAULT_EXPIRY_TIME = 60 * 60 * 1000
17
+ HEADERS = {
18
+ "Accept" => "application/json",
19
+ "Origin" => BASE_URL,
20
+ "Referer" => "#{BASE_URL}/zh-CN/login",
21
+ "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"
22
+ }.freeze
23
+
24
+ module_function
25
+
26
+ def random_value(prefix, size)
27
+ chars = ("a".."z").to_a + ("0".."9").to_a
28
+ "#{prefix}#{Array.new(size) { chars.sample }.join}"
29
+ end
30
+
31
+ def random_password
32
+ "Sdk!#{random_value('', 12)}A1"
33
+ end
34
+
35
+ def login_referer(base_url)
36
+ "#{base_url}/zh-CN/login"
37
+ end
38
+
39
+ def make_headers(base_url)
40
+ HEADERS.merge("Origin" => base_url, "Referer" => login_referer(base_url))
41
+ end
42
+
43
+ # 将会话信息编码为 token(base64 JSON)
44
+ def encode_token(cookie, email_id, base_url)
45
+ raw = JSON.generate({ "c" => cookie, "i" => email_id, "b" => base_url })
46
+ TOKEN_PREFIX + Base64.strict_encode64(raw)
47
+ end
48
+
49
+ # 从 token 解码出会话信息
50
+ def decode_token(token)
51
+ raise "zhujump: invalid session token" unless token.to_s.start_with?(TOKEN_PREFIX)
52
+
53
+ payload = token[TOKEN_PREFIX.length..]
54
+ data = JSON.parse(Base64.strict_decode64(payload))
55
+ cookie = data["c"].to_s.strip
56
+ email_id = data["i"].to_s.strip
57
+ raise "zhujump: invalid session token" if cookie.empty? || email_id.empty?
58
+
59
+ base_url = (data["b"] || BASE_URL).to_s.strip.chomp("/")
60
+ { "cookie" => cookie, "email_id" => email_id, "base_url" => base_url }
61
+ rescue ArgumentError, JSON::ParserError => e
62
+ raise "zhujump: invalid session token (#{e.message})"
63
+ end
64
+
65
+ # 使用标准库 Net::HTTP 实现带 cookie jar 的会话请求
66
+ # 此 provider 需要在注册/登录/创建邮箱的多步流程中保持 cookie
67
+ class Session
68
+ attr_reader :cookies
69
+
70
+ def initialize(base_url)
71
+ @base_url = base_url
72
+ @cookies = {}
73
+ config = Config.get_config
74
+ @timeout = config.timeout || 15
75
+ @proxy = config.proxy
76
+ @insecure = config.insecure
77
+ @extra_headers = config.headers || {}
78
+ end
79
+
80
+ def get(path, extra_headers: {})
81
+ do_request(:get, path, extra_headers: extra_headers)
82
+ end
83
+
84
+ def post(path, json: nil, form: nil, extra_headers: {})
85
+ do_request(:post, path, json: json, form: form, extra_headers: extra_headers)
86
+ end
87
+
88
+ private
89
+
90
+ def do_request(method, path, json: nil, form: nil, extra_headers: {})
91
+ uri = URI.parse("#{@base_url}#{path}")
92
+ http = build_http(uri)
93
+ req = build_req(method, uri, json, form, extra_headers)
94
+ resp = http.request(req)
95
+ # 收集 cookies
96
+ (resp.get_fields("set-cookie") || []).each do |sc|
97
+ kv = sc.split(";").first.to_s.strip.split("=", 2)
98
+ @cookies[kv[0]] = kv[1] if kv.length == 2
99
+ end
100
+ resp
101
+ end
102
+
103
+ def build_http(uri)
104
+ http = if @proxy && !@proxy.empty?
105
+ p = URI.parse(@proxy)
106
+ Net::HTTP.new(uri.host, uri.port, p.host, p.port, p.user, p.password)
107
+ else
108
+ Net::HTTP.new(uri.host, uri.port)
109
+ end
110
+ http.use_ssl = (uri.scheme == "https")
111
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @insecure && http.use_ssl?
112
+ http.open_timeout = @timeout
113
+ http.read_timeout = @timeout
114
+ http
115
+ end
116
+
117
+ def build_req(method, uri, json, form, extra_headers)
118
+ klass = method == :post ? Net::HTTP::Post : Net::HTTP::Get
119
+ req = klass.new(uri.request_uri)
120
+ # 设置 headers
121
+ hdrs = Zhujump.make_headers(@base_url)
122
+ @extra_headers.each { |k, v| req[k] = v }
123
+ hdrs.each { |k, v| req[k] = v }
124
+ extra_headers.each { |k, v| req[k] = v }
125
+ # Cookie
126
+ req["Cookie"] = cookie_header unless @cookies.empty?
127
+ # Body
128
+ if json
129
+ req.body = JSON.generate(json)
130
+ req["Content-Type"] = "application/json"
131
+ elsif form
132
+ req.body = URI.encode_www_form(form)
133
+ req["Content-Type"] = "application/x-www-form-urlencoded"
134
+ end
135
+ req
136
+ end
137
+
138
+ def cookie_header
139
+ @cookies.map { |k, v| "#{k}=#{v}" }.join("; ")
140
+ end
141
+ end
142
+
143
+ # @param domain [String] 邮箱域名
144
+ # @param channel [String] 渠道标识
145
+ # @return [EmailInfo]
146
+ def generate_email(domain, channel)
147
+ generate_email_for_instance(BASE_URL, domain, channel, DEFAULT_EXPIRY_TIME)
148
+ end
149
+
150
+ # @param base_url [String] 实例基础 URL
151
+ # @param domain [String] 邮箱域名
152
+ # @param channel [String] 渠道标识
153
+ # @param expiry_time [Integer, nil] 过期时间(毫秒)
154
+ # @return [EmailInfo]
155
+ def generate_email_for_instance(base_url, domain, channel, expiry_time = DEFAULT_EXPIRY_TIME)
156
+ base_url = base_url.chomp("/")
157
+ sess = Session.new(base_url)
158
+ username = random_value("sdk", 10)
159
+ password = random_password
160
+
161
+ # 注册
162
+ reg = sess.post("/api/auth/register",
163
+ json: { "username" => username, "password" => password, "turnstileToken" => "" })
164
+ raise "zhujump: register failed (#{reg.code})" unless reg.is_a?(Net::HTTPSuccess)
165
+
166
+ # 获取 CSRF
167
+ csrf_resp = sess.get("/api/auth/csrf")
168
+ raise "zhujump: csrf request failed" unless csrf_resp.is_a?(Net::HTTPSuccess)
169
+
170
+ csrf_data = JSON.parse(csrf_resp.body || "{}")
171
+ csrf = csrf_data["csrfToken"].to_s.strip
172
+ raise "zhujump: csrf token missing" if csrf.empty?
173
+
174
+ # 登录
175
+ login = sess.post("/api/auth/callback/credentials?",
176
+ form: {
177
+ "username" => username, "password" => password,
178
+ "turnstileToken" => "", "redirect" => "false",
179
+ "csrfToken" => csrf, "callbackUrl" => login_referer(base_url)
180
+ },
181
+ extra_headers: { "x-auth-return-redirect" => "1" })
182
+ # 允许 3xx 重定向也视为成功
183
+ code = login.code.to_i
184
+ raise "zhujump: login failed (#{code})" unless code >= 200 && code < 400
185
+
186
+ # 验证 session
187
+ session_resp = sess.get("/api/auth/session")
188
+ raise "zhujump: session check failed" unless session_resp.is_a?(Net::HTTPSuccess)
189
+
190
+ session_json = JSON.parse(session_resp.body || "{}")
191
+ got_user = (session_json.dig("user", "username") || "").strip
192
+ raise "zhujump: login verification failed" unless got_user == username
193
+
194
+ # 创建邮箱
195
+ local = random_value("sdk", 10)
196
+ body = { "name" => local, "domain" => domain }
197
+ body["expiryTime"] = expiry_time unless expiry_time.nil?
198
+ created = sess.post("/api/emails/generate", json: body)
199
+ raise "zhujump: generate email failed (#{created.code})" unless created.is_a?(Net::HTTPSuccess)
200
+
201
+ created_json = JSON.parse(created.body || "{}")
202
+ email_addr = created_json["email"].to_s.strip
203
+ email_id = created_json["id"].to_s.strip
204
+ raise "zhujump: invalid generate response" if email_addr.empty? || email_id.empty?
205
+
206
+ cookie = sess.cookies.map { |k, v| "#{k}=#{v}" }.join("; ")
207
+ EmailInfo.new(channel: channel, email: email_addr,
208
+ token: encode_token(cookie, email_id, base_url))
209
+ end
210
+
211
+ # @param token [String] 编码的会话 token
212
+ # @param email [String] 邮箱地址
213
+ # @return [Array<Email>]
214
+ def get_emails(token, email)
215
+ session = decode_token(token)
216
+ base_url = session["base_url"]
217
+ hdrs = make_headers(base_url).merge("Cookie" => session["cookie"])
218
+
219
+ resp = Http.get("#{base_url}/api/emails/#{session['email_id']}", headers: hdrs)
220
+ resp.raise_for_status
221
+ data = resp.json
222
+ rows = data.is_a?(Hash) ? data["messages"] : nil
223
+ return [] unless rows.is_a?(Array)
224
+
225
+ rows.filter_map do |item|
226
+ next unless item.is_a?(Hash)
227
+
228
+ source = item
229
+ message_id = item["id"].to_s.strip
230
+ if !message_id.empty? && item["content"].to_s.strip.empty? && item["html"].to_s.strip.empty?
231
+ begin
232
+ d = Http.get("#{base_url}/api/emails/#{session['email_id']}/#{message_id}", headers: hdrs)
233
+ if d.ok?
234
+ detail_data = d.json
235
+ msg = detail_data.is_a?(Hash) ? detail_data["message"] : nil
236
+ source = item.merge(msg) if msg.is_a?(Hash)
237
+ end
238
+ rescue StandardError
239
+ # 忽略详情失败
240
+ end
241
+ end
242
+ Normalize.normalize_email({
243
+ "id" => source["id"] || "",
244
+ "from_address" => source["from_address"] || "",
245
+ "to_address" => source["to_address"] || email,
246
+ "subject" => source["subject"] || "",
247
+ "content" => source["content"] || "",
248
+ "html" => source["html"] || "",
249
+ "received_at" => source["received_at"] || source["sent_at"],
250
+ "isRead" => false
251
+ }, email)
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end