Module: TempmailSdk::Providers::Ockito

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

Overview

ockito 渠道实现 API: https://ockito.com/web-api 流程: 获取 gtoken -> 获取邮箱 -> 用 Bearer token 收信

Constant Summary collapse

CHANNEL =
"ockito"
BASE_URL =
"https://ockito.com/web-api"
DEFAULT_HEADERS =
{
  "Accept" => "application/json",
  "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"
}.freeze

Class Method Summary collapse

Class Method Details

.fetch_bearer_json(path, access_token, refresh_token) ⇒ Object

带 Bearer 认证的请求(401 时自动刷新重试)



70
71
72
73
74
75
76
77
78
79
# File 'lib/tempmail_sdk/providers/ockito.rb', line 70

def fetch_bearer_json(path, access_token, refresh_token)
  _, data = fetch_json(path, headers: { "Authorization" => "Bearer #{access_token}" })
  data
rescue RuntimeError => e
  raise unless e.message.include?("401")

  refreshed = refresh_access_token(refresh_token)
  _, data = fetch_json(path, headers: { "Authorization" => "Bearer #{refreshed}" })
  data
end

.fetch_json(path, method: :get, headers: nil, payload: nil) ⇒ Object

请求并解析 JSON



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/tempmail_sdk/providers/ockito.rb', line 22

def fetch_json(path, method: :get, headers: nil, payload: nil)
  request_headers = DEFAULT_HEADERS.dup
  request_headers.merge!(headers) if headers

  resp = if method == :post
           Http.post("#{BASE_URL}#{path}", headers: request_headers, json: payload || {})
         else
           Http.get("#{BASE_URL}#{path}", headers: request_headers)
         end

  text = resp.body || ""
  data = text.empty? ? {} : JSON.parse(text)
  raise "ockito http #{resp.status_code}" unless resp.ok?

  [resp.status_code, data.is_a?(Hash) ? data : {}]
end

.flatten_inbox_row(raw, recipient) ⇒ Object

扁平化收件箱行



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/tempmail_sdk/providers/ockito.rb', line 82

def flatten_inbox_row(raw, recipient)
  {
    "id" => (raw["uid"] || "").to_s,
    "from" => (raw["sender"] || "").to_s,
    "to" => recipient,
    "subject" => (raw["subject"] || "").to_s,
    "text" => (raw["snippet"] || "").to_s,
    "html" => (raw["html"] || "").to_s,
    "date" => (raw["timestamp"] || "").to_s,
    "is_read" => false,
    "attachments" => []
  }
end

.flatten_message(raw, recipient) ⇒ Object

扁平化邮件详情



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

def flatten_message(raw, recipient)
  obj = raw["obj"].is_a?(Hash) ? raw["obj"] : raw
  {
    "id" => (raw["uid"] || "").to_s,
    "from" => (obj["sender_email"] || obj["SenderEmail"] || obj["from_"] || obj["From"] || obj["from"] || obj["sender_name"] || obj["SenderName"] || "").to_s,
    "to" => (obj["to"] || obj["To"] || recipient).to_s,
    "subject" => (obj["subject"] || obj["Subject"] || "").to_s,
    "text" => (obj["text"] || "").to_s,
    "html" => (obj["html"] || "").to_s,
    "date" => (obj["date"] || obj["Date"] || "").to_s,
    "is_read" => false,
    "attachments" => []
  }
end

.generate_emailEmailInfo

创建临时邮箱

Returns:



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

def generate_email
  _,  = fetch_json("/gtoken", method: :post, payload: {})
  access_token = ["access_token"].to_s.strip
  refresh_token = ["refresh_token"].to_s.strip
  raise "ockito: 无效的 token 响应" if access_token.empty? || refresh_token.empty?

  _, email_data = fetch_json("/email", headers: { "Authorization" => "Bearer #{access_token}" })
  email_value = email_data["email"]
  email = if email_value.is_a?(String)
            email_value.strip
          elsif email_value.is_a?(Hash)
            (email_value["email"] || "").to_s.strip
          else
            ""
          end
  raise "ockito: 无效的邮箱响应" if email.empty? || !email.include?("@")

  EmailInfo.new(channel: CHANNEL, email: email, token: pack_token(access_token, refresh_token))
end

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

获取邮件列表

Parameters:

  • token (String)

    JSON 编码的 access+refresh token

  • email (String)

Returns:



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/tempmail_sdk/providers/ockito.rb', line 138

def get_emails(token, email)
  access_token, refresh_token = unpack_token(token)
  address = email.to_s.strip
  raise "ockito: 邮箱地址为空" if address.empty?

  data = fetch_bearer_json("/retrieve/#{URI.encode_www_form_component(address)}/emails", access_token, refresh_token)
  rows = data["inbox"]
  return [] unless rows.is_a?(Array)

  rows.select { |row| row.is_a?(Hash) }.map do |row|
    uid = row["uid"].to_s.strip
    if uid.empty?
      Normalize.normalize_email(flatten_inbox_row(row, address), address)
    else
      begin
        detail = fetch_bearer_json("/retrieve/#{URI.encode_www_form_component(address)}/#{URI.encode_www_form_component(uid)}", access_token, refresh_token)
        Normalize.normalize_email(flatten_message(detail, address), address)
      rescue StandardError
        Normalize.normalize_email(flatten_inbox_row(row, address), address)
      end
    end
  end
end

.pack_token(access_token, refresh_token) ⇒ Object

打包 token



40
41
42
# File 'lib/tempmail_sdk/providers/ockito.rb', line 40

def pack_token(access_token, refresh_token)
  JSON.generate({ "access_token" => access_token, "refresh_token" => refresh_token })
end

.refresh_access_token(refresh_token) ⇒ Object

刷新 access token



60
61
62
63
64
65
66
67
# File 'lib/tempmail_sdk/providers/ockito.rb', line 60

def refresh_access_token(refresh_token)
  _, data = fetch_json("/grefresh", method: :post,
                                    headers: { "Authorization" => "Bearer #{refresh_token}", "X-PASSTHROUGH" => "Y" })
  access = data["access_token"].to_s.strip
  raise "ockito: 刷新 token 失败" if access.empty?

  access
end

.unpack_token(token) ⇒ Object

解包 token



45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/tempmail_sdk/providers/ockito.rb', line 45

def unpack_token(token)
  value = token.to_s.strip
  raise "ockito: 无效的会话 token" if value.empty? || !value.start_with?("{")

  data = JSON.parse(value)
  raise "ockito: 无效的会话 token" unless data.is_a?(Hash)

  access = data["access_token"].to_s.strip
  refresh = data["refresh_token"].to_s.strip
  raise "ockito: 无效的会话 token" if access.empty? || refresh.empty?

  [access, refresh]
end