Module: TempmailSdk::Providers::Moakt
- Defined in:
- lib/tempmail_sdk/providers/moakt.rb
Overview
moakt.com 渠道实现(HTML 抓取型)
流程:
- GET 语言首页,收集初始 Cookie 与可用域名
- POST /locale/inbox 创建邮箱(随机域名或指定域名),从 302 响应捕获 tm_session Cookie;
- GET /locale/inbox 解析 #email-address 得到邮箱地址;
- 会话凭证(locale + 序列化 Cookie 头)经 base64 打包进 token;
- 收信时列表解析 /locale/email/uuid,正文 GET .../html 解析 .email-body。
多个渠道别名(moakt.cc/moakt.co/moakt.ws/tmail.ws 等)共用此实现,仅固定域名不同。
Constant Summary collapse
- CHANNEL =
"moakt"- ORIGIN =
"https://www.moakt.com"- TOK_PREFIX =
"mok1:"- DEFAULT_UA =
默认 User-Agent(config.headers 未指定时使用)
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " \ "(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0"
- DEFAULT_HEADERS =
页面请求的固定基础头
{ "Accept-Language" => "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", "Cache-Control" => "no-cache", "DNT" => "1", "Pragma" => "no-cache", "Upgrade-Insecure-Requests" => "1" }.freeze
- EMAIL_DIV_RE =
/<div\s+id="email-address"\s*>([^<]+)<\/div>/im- DOMAIN_OPTION_RE =
/<option\s+value="([^"]+)">\s*@[^<]+<\/option>/im- MAIL_DOMAIN_RE =
/\A[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+\z/i- HREF_EMAIL_RE =
/href="(\/[^"]+\/email\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"/- TITLE_RE =
/<li\s+class="title"\s*>([^<]*)<\/li>/im- DATE_RE =
/<li\s+class="date"[^>]*>[\s\S]*?<span[^>]*>([^<]+)<\/span>/im- SENDER_RE =
/<li\s+class="sender"[^>]*>[\s\S]*?<span[^>]*>([\s\S]*?)<\/span>\s*<\/li>/im- BODY_RE =
/<div\s+class="email-body"\s*>([\s\S]*?)<\/div>/im- FROM_ADDR_RE =
/<([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>/- TAG_RE =
/<[^>]+>/
Class Method Summary collapse
-
.cookies_from_response(resp) ⇒ Object
从响应的 Set-Cookie 头提取本次新设置的 Cookie.
-
.decode_sess(tok) ⇒ Object
解码 token,返回 [locale, cookie_hdr].
-
.email_domain(email) ⇒ Object
提取邮箱地址的域名部分(小写).
-
.encode_sess(locale, cookie_hdr) ⇒ Object
将会话(locale + Cookie 头)编码为不透明 token.
-
.generate_email(domain = nil) ⇒ EmailInfo
创建临时邮箱.
-
.get_emails(email, token) ⇒ Array<Email>
收取邮件列表.
-
.list_mail_ids(html_s) ⇒ Object
从收件箱页提取邮件 UUID 列表(去重、排除删除链接).
-
.merge_cookie_hdr(prev, resp) ⇒ Object
合并旧 Cookie 头与响应新设置的 Cookie,按 key 排序拼接(保证稳定).
-
.page_headers(referer, ua) ⇒ Object
构造页面请求头.
-
.parse_cookie_map(hdr) ⇒ Object
将 Cookie 头字符串解析为 name => value 映射.
-
.parse_detail(page, mid, recipient) ⇒ Object
解析邮件详情页,返回标准化前的原始 Hash.
-
.parse_inbox_email(html_s) ⇒ Object
从收件箱页解析邮箱地址.
-
.parse_server_domains(page) ⇒ Object
从首页 HTML 收集服务端提供的可用域名集合.
-
.random_local(length = 12) ⇒ Object
生成随机本地部分(小写字母 + 数字).
-
.request_parts(domain) ⇒ Object
解析域名参数,返回 [locale, mail_domain] - 空或含非法字符:语言页取默认 "zh",随机域名 - 形如域名:locale="zh",使用指定域名 - 其它字符串:视为 locale,随机域名.
-
.strip_tags(s) ⇒ Object
去除 HTML 标签,压缩为文本.
-
.user_agent ⇒ Object
从 config 取 UA,缺省用内置默认值.
Class Method Details
.cookies_from_response(resp) ⇒ Object
从响应的 Set-Cookie 头提取本次新设置的 Cookie
97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 97 def (resp) map = {} resp..each do |line| pair = line.split(";", 2).first.to_s.strip next unless pair.include?("=") k, v = pair.split("=", 2) k = k.strip map[k] = v.to_s.strip unless k.empty? end map end |
.decode_sess(tok) ⇒ Object
解码 token,返回 [locale, cookie_hdr]
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 139 def decode_sess(tok) raise "moakt: invalid session token" unless tok.to_s.start_with?(TOK_PREFIX) data = Base64.strict_decode64(tok[TOK_PREFIX.length..]) o = JSON.parse(data) loc = o["l"].to_s.strip c = o["c"].to_s.strip raise "moakt: invalid session token" if loc.empty? || c.empty? [loc, c] rescue ArgumentError, JSON::ParserError raise "moakt: invalid session token" end |
.email_domain(email) ⇒ Object
提取邮箱地址的域名部分(小写)
77 78 79 80 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 77 def email_domain(email) _head, sep, domain = email.to_s.rpartition("@") sep.empty? ? "" : domain.strip.downcase end |
.encode_sess(locale, cookie_hdr) ⇒ Object
将会话(locale + Cookie 头)编码为不透明 token
133 134 135 136 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 133 def encode_sess(locale, ) raw = JSON.generate("l" => locale, "c" => ) TOK_PREFIX + Base64.strict_encode64(raw) end |
.generate_email(domain = nil) ⇒ EmailInfo
创建临时邮箱
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 219 def generate_email(domain = nil) loc, mail_domain = request_parts(domain) base = "#{ORIGIN}/#{loc}" inbox = "#{base}/inbox" ua = user_agent r1 = Http.get(base, headers: page_headers(base, ua)) r1.raise_for_status = ("", r1) if mail_domain.empty? post_data = { "random" => "1" } else unless parse_server_domains(r1.body).include?(mail_domain) raise "moakt: unsupported domain #{mail_domain}" end post_data = { "setemail" => "", "username" => random_local, "domain" => mail_domain, "preferred_domain" => "" } end # POST /inbox 创建邮箱,从 302 响应捕获 tm_session Cookie(Net::HTTP 默认不跟随重定向) r2 = Http.post( inbox, headers: page_headers(base, ua).merge( "Content-Type" => "application/x-www-form-urlencoded", "Cookie" => ), body: URI.encode_www_form(post_data) ) = (, r2) unless ().key?("tm_session") raise "moakt: missing tm_session cookie" end # GET /inbox 获取邮箱地址 r3 = Http.get(inbox, headers: page_headers(base, ua).merge("Cookie" => )) r3.raise_for_status = (, r3) email = parse_inbox_email(r3.body) if !mail_domain.empty? && email_domain(email) != mail_domain raise "moakt: domain mismatch expected=#{mail_domain} actual=#{email_domain(email)}" end EmailInfo.new(channel: CHANNEL, email: email, token: encode_sess(loc, )) end |
.get_emails(email, token) ⇒ Array<Email>
收取邮件列表
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 276 def get_emails(email, token) loc, = decode_sess(token) inbox = "#{ORIGIN}/#{loc}/inbox" base_ref = "#{ORIGIN}/#{loc}" ua = user_agent r = Http.get(inbox, headers: page_headers(base_ref, ua).merge("Cookie" => )) r.raise_for_status list_mail_ids(r.body).filter_map do |mid| detail = "#{ORIGIN}/#{loc}/email/#{mid}/html" refer = "#{ORIGIN}/#{loc}/email/#{mid}" rd = Http.get(detail, headers: page_headers(refer, ua).merge("Cookie" => )) next unless rd.status_code == 200 Normalize.normalize_email(parse_detail(rd.body, mid, email), email) end end |
.list_mail_ids(html_s) ⇒ Object
从收件箱页提取邮件 UUID 列表(去重、排除删除链接)
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 170 def list_mail_ids(html_s) seen = {} out = [] html_s.scan(HREF_EMAIL_RE).each do |cap| path = cap[0] next if path.include?("/delete") mid = path.split("/").last if mid.length == 36 && !seen[mid] seen[mid] = true out << mid end end out end |
.merge_cookie_hdr(prev, resp) ⇒ Object
合并旧 Cookie 头与响应新设置的 Cookie,按 key 排序拼接(保证稳定)
111 112 113 114 115 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 111 def (prev, resp) d = (prev) d.merge!((resp)) d.keys.sort.map { |k| "#{k}=#{d[k]}" }.join("; ") end |
.page_headers(referer, ua) ⇒ Object
构造页面请求头
118 119 120 121 122 123 124 125 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 118 def page_headers(referer, ua) DEFAULT_HEADERS.merge( "User-Agent" => ua, "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9," \ "image/avif,image/webp,image/apng,*/*;q=0.8", "Referer" => referer ) end |
.parse_cookie_map(hdr) ⇒ Object
将 Cookie 头字符串解析为 name => value 映射
83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 83 def (hdr) map = {} hdr.to_s.split(";").each do |part| part = part.strip next unless part.include?("=") k, v = part.split("=", 2) k = k.strip map[k] = v.to_s.strip unless k.empty? end map end |
.parse_detail(page, mid, recipient) ⇒ Object
解析邮件详情页,返回标准化前的原始 Hash
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 187 def parse_detail(page, mid, recipient) from_s = "" if (sm = page.match(SENDER_RE)) inner = CGI.unescapeHTML(sm[1]) from_s = (inner) if (em = inner.match(FROM_ADDR_RE)) from_s = em[1].strip end end subj = "" subj = CGI.unescapeHTML(page.match(TITLE_RE)[1].strip) if page.match?(TITLE_RE) date_s = "" date_s = CGI.unescapeHTML(page.match(DATE_RE)[1].strip) if page.match?(DATE_RE) body = "" body = page.match(BODY_RE)[1].strip if page.match?(BODY_RE) { "id" => mid, "to" => recipient, "from" => from_s, "subject" => subj, "date" => date_s, "html" => body } end |
.parse_inbox_email(html_s) ⇒ Object
从收件箱页解析邮箱地址
154 155 156 157 158 159 160 161 162 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 154 def parse_inbox_email(html_s) m = html_s.match(EMAIL_DIV_RE) raise "moakt: email-address not found" unless m addr = CGI.unescapeHTML(m[1].strip) raise "moakt: empty email-address" if addr.empty? addr end |
.parse_server_domains(page) ⇒ Object
从首页 HTML 收集服务端提供的可用域名集合
64 65 66 67 68 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 64 def parse_server_domains(page) page.scan(DOMAIN_OPTION_RE) .map { |m| m[0].strip.sub(/\A@/, "").downcase } .reject(&:empty?) end |
.random_local(length = 12) ⇒ Object
生成随机本地部分(小写字母 + 数字)
71 72 73 74 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 71 def random_local(length = 12) chars = ("a".."z").to_a + ("0".."9").to_a Array.new(length) { chars.sample }.join end |
.request_parts(domain) ⇒ Object
解析域名参数,返回 [locale, mail_domain]
- 空或含非法字符:语言页取默认 "zh",随机域名
- 形如域名:locale="zh",使用指定域名
- 其它字符串:视为 locale,随机域名
55 56 57 58 59 60 61 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 55 def request_parts(domain) s = domain.to_s.strip return ["zh", ""] if s.empty? || s.match?(/[\/?#\\]/) return ["zh", s.sub(/\A@/, "").downcase] if s.match?(MAIL_DOMAIN_RE) [s, ""] end |
.strip_tags(s) ⇒ Object
去除 HTML 标签,压缩为文本
165 166 167 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 165 def (s) s.gsub(TAG_RE, " ").strip end |
.user_agent ⇒ Object
从 config 取 UA,缺省用内置默认值
128 129 130 |
# File 'lib/tempmail_sdk/providers/moakt.rb', line 128 def user_agent (Config.get_config.headers || {})["User-Agent"] || DEFAULT_UA end |