Module: TempmailSdk::Client

Defined in:
lib/tempmail_sdk/client.rb

Overview

SDK 主入口:聚合所有渠道逻辑,提供统一的无状态 API 与有状态 Client

Class Method Summary collapse

Class Method Details

.all_channelsObject

所有支持的公共渠道标识(有序),由注册表派生



9
10
11
# File 'lib/tempmail_sdk/client.rb', line 9

def all_channels
  Registry::REGISTRY.map(&:channel)
end

.build_channel_order(preferred = nil) ⇒ Object

构建渠道尝试顺序:指定渠道优先,其余打乱追加;未指定则全部打乱 返回值为本端运行时随机顺序,不作为跨 SDK 一致性约束



87
88
89
90
91
92
# File 'lib/tempmail_sdk/client.rb', line 87

def build_channel_order(preferred = nil)
  shuffled = all_channels.shuffle
  return shuffled unless preferred

  [preferred] + shuffled.reject { |ch| ch == preferred }
end

.channel_info(channel) ⇒ ChannelInfo?

获取指定渠道的详细信息

Parameters:

  • channel (String)

Returns:



30
31
32
# File 'lib/tempmail_sdk/client.rb', line 30

def channel_info(channel)
  channel_info_map[channel]
end

.channel_info_mapObject

渠道信息映射表,由注册表派生



14
15
16
17
18
# File 'lib/tempmail_sdk/client.rb', line 14

def channel_info_map
  @channel_info_map ||= Registry::REGISTRY.each_with_object({}) do |spec, h|
    h[spec.channel] = ChannelInfo.new(channel: spec.channel, name: spec.name, website: spec.website)
  end
end

.generate_email(options = nil) ⇒ EmailInfo?

创建临时邮箱

错误处理策略:

  • 指定渠道失败时,自动尝试其他可用渠道(打乱顺序逐个尝试)
  • 未指定渠道时,打乱全部渠道逐个尝试,直到成功
  • 所有渠道均不可用时返回 nil(不抛出异常)

Parameters:

Returns:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/tempmail_sdk/client.rb', line 43

def generate_email(options = nil)
  options ||= GenerateEmailOptions.new
  logger = Log.logger
  try_order = build_channel_order(options.channel)

  max_channels = options.max_channels_tried.to_i.positive? ? options.max_channels_tried : 20
  total_timeout = options.total_timeout.to_f.positive? ? options.total_timeout : 60.0
  start_time = Time.now
  channels_tried = 0
  last_err = ""

  try_order.each do |ch|
    if channels_tried >= max_channels
      logger.warn("已尝试最大渠道数 (#{max_channels}),停止")
      break
    end
    if Time.now - start_time >= total_timeout
      logger.warn("整体超时,停止尝试")
      break
    end

    channels_tried += 1
    logger.info("创建临时邮箱, 渠道: #{ch}")
    r = Retry.with_retry_with_attempts(options.retry_config) { generate_email_once(ch, options) }
    if r.ok?
      result = r.value
      logger.info("邮箱创建成功: #{result.email} (渠道: #{ch})")
      Telemetry.report(operation: "generate_email", channel: ch, success: true,
                       attempt_count: r.attempts, channels_tried: channels_tried, error: "")
      return result
    end

    last_err = r.error ? r.error.message.to_s : "unknown"
    logger.warn("渠道 #{ch} 不可用: #{last_err},尝试下一个渠道")
  end

  logger.error("所有渠道均不可用,创建邮箱失败")
  Telemetry.report(operation: "generate_email", channel: "", success: false,
                   attempt_count: 0, channels_tried: channels_tried, error: last_err)
  nil
end

.generate_email_once(channel, options) ⇒ Object

单次创建邮箱(不含重试逻辑)



95
96
97
98
99
100
# File 'lib/tempmail_sdk/client.rb', line 95

def generate_email_once(channel, options)
  spec = Registry::REGISTRY_MAP[channel]
  raise "Unknown channel: #{channel}" if spec.nil?

  spec.generate.call(options)
end

.get_emails(info, options = nil) ⇒ GetEmailsResult

获取邮件列表 Channel/Email/Token 等由 SDK 从 EmailInfo 中自动获取,用户无需手动传递

Parameters:

Returns:



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/tempmail_sdk/client.rb', line 108

def get_emails(info, options = nil)
  if info.nil?
    Telemetry.report(operation: "get_emails", channel: "", success: false, attempt_count: 0,
                     channels_tried: 0, error: "EmailInfo is required, call generate_email() first")
    raise ArgumentError, "EmailInfo is required, call generate_email() first"
  end

  channel = info.channel
  email = info.email
  token = info.token
  retry_config = options&.retry_config
  logger = Log.logger

  if channel.nil? || channel.empty?
    Telemetry.report(operation: "get_emails", channel: "", success: false, attempt_count: 0,
                     channels_tried: 0, error: "channel is required")
    raise ArgumentError, "channel is required"
  end
  if (email.nil? || email.empty?) && channel != "tempmail-lol"
    Telemetry.report(operation: "get_emails", channel: channel, success: false, attempt_count: 0,
                     channels_tried: 0, error: "email is required")
    raise ArgumentError, "email is required"
  end

  logger.debug("获取邮件, 渠道: #{channel}, 邮箱: #{email}")
  r = Retry.with_retry_with_attempts(retry_config) { get_emails_once(channel, email, token) }

  if r.ok?
    emails = r.value
    if emails.empty?
      logger.debug("暂无邮件, 渠道: #{channel}")
    else
      logger.info("获取到 #{emails.size} 封邮件, 渠道: #{channel}")
    end
    Telemetry.report(operation: "get_emails", channel: channel, success: true,
                     attempt_count: r.attempts, channels_tried: 0, error: "")
    return GetEmailsResult.new(channel: channel, email: email, emails: emails, success: true)
  end

  err_s = r.error ? r.error.message.to_s : "unknown"
  logger.error("获取邮件失败, 渠道: #{channel}, 错误: #{err_s}")
  Telemetry.report(operation: "get_emails", channel: channel, success: false,
                   attempt_count: r.attempts, channels_tried: 0, error: err_s)
  GetEmailsResult.new(channel: channel, email: email, emails: [], success: false)
end

.get_emails_once(channel, email, token) ⇒ Object

单次获取邮件(不含重试逻辑)



155
156
157
158
159
160
# File 'lib/tempmail_sdk/client.rb', line 155

def get_emails_once(channel, email, token)
  spec = Registry::REGISTRY_MAP[channel]
  raise "Unknown channel: #{channel}" if spec.nil?

  spec.get_emails.call(email, token)
end

.list_channelsArray<ChannelInfo>

获取所有支持的渠道列表(有序,与 baseline 逐行一致)

Returns:



22
23
24
25
# File 'lib/tempmail_sdk/client.rb', line 22

def list_channels
  map = channel_info_map
  all_channels.map { |ch| map[ch] }
end