Module: TempmailSdk::Providers::Dropmail

Defined in:
lib/tempmail_sdk/providers/dropmail.rb

Overview

dropmail.me GraphQL 渠道 — https://dropmail.me 使用 af_ 令牌访问 GraphQL API 创建 session 和查询邮件

Constant Summary collapse

CHANNEL =
"dropmail"
TOKEN_GENERATE_URL =
"https://dropmail.me/api/token/generate"
TOKEN_RENEW_URL =
"https://dropmail.me/api/token/renew"
CREATE_SESSION_QUERY =
"mutation {introduceSession {id, expiresAt, addresses{id, address}}}"
GET_MAILS_QUERY =
'query ($id: ID!) { session(id:$id) { mails { id, rawSize, fromAddr, toAddr, receivedAt, text, headerFrom, headerSubject, html } } }'
TOKEN_HEADERS =
{
  "Accept" => "application/json",
  "Content-Type" => "application/json",
  "Origin" => "https://dropmail.me",
  "Referer" => "https://dropmail.me/api/"
}.freeze
AUTO_CACHE_SEC =
50 * 60
RENEW_BEFORE_SEC =
10 * 60

Class Method Summary collapse

Class Method Details

.explicit_af_tokenObject

从配置或环境变量中获取显式 token



34
35
36
37
38
39
# File 'lib/tempmail_sdk/providers/dropmail.rb', line 34

def explicit_af_token
  ENV["DROPMAIL_AUTH_TOKEN"].to_s.strip.then do |t|
    return t unless t.empty?
  end
  ENV["DROPMAIL_API_TOKEN"].to_s.strip
end

.fetch_af_tokenObject

从 API 获取新 af_ token



42
43
44
45
46
47
48
49
50
# File 'lib/tempmail_sdk/providers/dropmail.rb', line 42

def fetch_af_token
  resp = Http.post(TOKEN_GENERATE_URL, headers: TOKEN_HEADERS, json: { "type" => "af", "lifetime" => "1h" })
  resp.raise_for_status
  body = resp.json
  tok = (body["token"] || "").to_s.strip
  raise "DropMail token/generate 未返回有效 af_ 令牌" if tok.empty? || !tok.start_with?("af_")

  tok
end

.generate_emailEmailInfo

创建临时邮箱

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/tempmail_sdk/providers/dropmail.rb', line 97

def generate_email
  data = graphql_request(CREATE_SESSION_QUERY)
  session = data["introduceSession"]
  raise "Failed to generate email" unless session && session["addresses"]&.any?

  EmailInfo.new(
    channel: CHANNEL,
    email: session["addresses"][0]["address"],
    token: session["id"],
    expires_at: session["expiresAt"]
  )
end

.get_emails(token, email = "") ⇒ Array<Email>

获取邮件列表

Parameters:

  • token (String)

    session ID

  • email (String) (defaults to: "")

Returns:



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/tempmail_sdk/providers/dropmail.rb', line 114

def get_emails(token, email = "")
  data = graphql_request(GET_MAILS_QUERY, { "id" => token })
  mails = ((data["session"] || {})["mails"]) || []
  mails.map do |m|
    raw = {
      "id" => m["id"] || "",
      "from" => m["fromAddr"] || "",
      "to" => m["toAddr"] || email,
      "subject" => m["headerSubject"] || "",
      "text" => m["text"] || "",
      "html" => m["html"] || "",
      "received_at" => m["receivedAt"] || "",
      "attachments" => []
    }
    Normalize.normalize_email(raw, email)
  end
end

.graphql_request(query, variables = nil) ⇒ Object

发送 GraphQL 请求



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/tempmail_sdk/providers/dropmail.rb', line 77

def graphql_request(query, variables = nil)
  data = { "query" => query }
  data["variables"] = JSON.generate(variables) if variables

  resp = Http.post(
    graphql_url,
    headers: { "Content-Type" => "application/x-www-form-urlencoded" },
    body: URI.encode_www_form(data)
  )
  resp.raise_for_status
  result = resp.json
  if result["errors"]&.any?
    raise "GraphQL error: #{result['errors'].first['message'] || 'Unknown'}"
  end

  result["data"] || {}
end

.graphql_urlObject

构造 GraphQL URL



71
72
73
74
# File 'lib/tempmail_sdk/providers/dropmail.rb', line 71

def graphql_url
  af = resolve_af_token
  "https://dropmail.me/api/graphql/#{URI.encode_www_form_component(af)}"
end

.resolve_af_tokenObject

解析 af_ token(优先显式配置,其次自动获取并缓存)



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/tempmail_sdk/providers/dropmail.rb', line 53

def resolve_af_token
  ex = explicit_af_token
  return ex unless ex.empty?

  now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  @lock.synchronize do
    if @cached_af
      tok, exp = @cached_af
      return tok if now < exp - RENEW_BEFORE_SEC
    end
    @cached_af = nil
    new_tok = fetch_af_token
    @cached_af = [new_tok, Process.clock_gettime(Process::CLOCK_MONOTONIC) + AUTO_CACHE_SEC]
    new_tok
  end
end