bzapper 0.7.1 → 0.8.1
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.
- checksums.yaml +4 -4
- data/README.md +69 -4
- data/lib/bzapper/codec.rb +7 -1
- data/lib/bzapper/resources/accounts.rb +27 -0
- data/lib/bzapper/resources/base.rb +6 -0
- data/lib/bzapper/resources/contacts.rb +27 -0
- data/lib/bzapper/resources/contacts_export.rb +89 -0
- data/lib/bzapper/transport.rb +10 -3
- data/lib/bzapper/version.rb +1 -1
- data/lib/bzapper.rb +2 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2492fe46b1c91f8b88b673e102de183388ec0aeba9686f83de3d62b50eb4cc01
|
|
4
|
+
data.tar.gz: 57c3253532c2a83d67cbbc21b811fc05f97a2c7ac331462fc7d4b60f4bb8a0c2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60d10efd60798233d8c40d2f006da6d322d38af941a86d06cb148230ec6cf3a9df435a70a1db49f2630319781b76199e75fac7c2d736bdaf38a2e943279bd926
|
|
7
|
+
data.tar.gz: cfee6f2f48d55e3a40934cb509e85ac5832b42f76c1e677dd17d8f8f0f5384f0b617cc22ba25535c3375f7591ef8f959fc90a56cf3acd523696c170fab569287
|
data/README.md
CHANGED
|
@@ -10,13 +10,13 @@ Zero dependências de runtime (só biblioteca padrão: `net/http`, `json`, `open
|
|
|
10
10
|
## Instalação
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
gem install bzapper -v 0.
|
|
13
|
+
gem install bzapper -v 0.8.1
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Ou no `Gemfile` — **fixe a versão exata** (cada release declara se muda a superfície pública):
|
|
17
17
|
|
|
18
18
|
```ruby
|
|
19
|
-
gem "bzapper", "0.
|
|
19
|
+
gem "bzapper", "0.8.1"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Hello world
|
|
@@ -53,6 +53,27 @@ client = Bzapper::Client.new(
|
|
|
53
53
|
Construir o cliente não faz nenhuma chamada de rede. Um cliente pode ser compartilhado entre
|
|
54
54
|
threads.
|
|
55
55
|
|
|
56
|
+
### Trocar a chave sem derrubar a integração (rotação)
|
|
57
|
+
|
|
58
|
+
`rotate_my_key` (admin) cria uma chave **nova** herdando papel, escopos, projeto e nome da
|
|
59
|
+
antiga, e mantém a antiga funcionando por um período de graça — dá tempo de fazer o deploy sem
|
|
60
|
+
janela de erro. A chave crua aparece **uma única vez**, no retorno. Depois do prazo a antiga
|
|
61
|
+
responde `401 key_expired`; `revoke_in_seconds: 0` revoga na hora (padrão 86400 s, máximo 30
|
|
62
|
+
dias). Chave de parceiro (bZapper Connect) roda pelo `PartnerClient#rotate_partner_connection_key`.
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
rotated = client.accounts.rotate_my_key(key_id, revoke_in_seconds: 3600) # 1 h de graça
|
|
66
|
+
|
|
67
|
+
rotated["api_key"] # a chave NOVA, crua — guarde agora, não volta
|
|
68
|
+
rotated["key"]["id"] # metadados da nova
|
|
69
|
+
rotated["previous_key"]["expires_at"] # quando a antiga para de funcionar (nil se revogada já)
|
|
70
|
+
rotated["old_key_expires_at"] # o mesmo instante, no topo do retorno
|
|
71
|
+
rotated["previous_key"]["rotated_to"] # id da chave que substituiu a antiga
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Uma chave já revogada ou já vencida responde `409` (`key_already_revoked` /
|
|
75
|
+
`key_already_expired`).
|
|
76
|
+
|
|
56
77
|
## Como os métodos funcionam
|
|
57
78
|
|
|
58
79
|
Os métodos ficam em **recursos**, um por área da API, e se chamam como o `operationId` da
|
|
@@ -66,13 +87,13 @@ spec OpenAPI em snake_case (`sendText` → `send_text`):
|
|
|
66
87
|
| `client.groups` | `list_groups`, `create_group`, `get_group`, `update_group`, `update_group_participants`, `group_invite_link`, `join_group`, `preview_group_invite`, `leave_group`, pedidos de entrada |
|
|
67
88
|
| `client.conversations` | `list_conversations`, `conversation_history` |
|
|
68
89
|
| `client.advanced` | editar/apagar/encaminhar mensagem, perfil, privacidade, arquivar/fixar/ler/silenciar chat, etiquetas, bloqueio, chamadas |
|
|
69
|
-
| `client.contacts` | CRM (`list_contacts`, `create_contact`, `update_contact`…), tags, grupos de contatos, opt-in/out, supressões, `contacts_check` |
|
|
90
|
+
| `client.contacts` | CRM (`list_contacts`, `create_contact`, `update_contact`…), `import_contacts` (lote), `export_contacts` (CSV), tags, grupos de contatos, opt-in/out, supressões, `contacts_check` |
|
|
70
91
|
| `client.campaigns` | `create_campaign`, `estimate_campaign`, destinatários, `dry_run_campaign`, `start/pause/resume/cancel_campaign` |
|
|
71
92
|
| `client.pools` | pools de números |
|
|
72
93
|
| `client.webhooks` | `create_webhook`, `update_webhook`, `test_webhook`, `list_webhook_deliveries`… |
|
|
73
94
|
| `client.advisories` | avisos "atualize sua integração" |
|
|
74
95
|
| `client.usage` / `client.billing` | consumo; plano, assinatura, add-ons, faturas, preços |
|
|
75
|
-
| `client.accounts` | perfil, chaves de API, marca, projetos, usuários |
|
|
96
|
+
| `client.accounts` | perfil, chaves de API (`rotate_my_key`), marca, projetos, usuários |
|
|
76
97
|
| `client.connect` | apps parceiros conectados à sua conta |
|
|
77
98
|
| `client.system` | `get_health` |
|
|
78
99
|
|
|
@@ -88,6 +109,8 @@ Convenções (iguais em todos os métodos):
|
|
|
88
109
|
vêm como `{"data" => [...], ...}` (a SDK não desembrulha, para não perder paginação e
|
|
89
110
|
metadados). Campos novos da API aparecem sem quebrar nada. `204` → `nil`.
|
|
90
111
|
* Parâmetro de caminho vazio, `"."` ou `".."` → `ArgumentError` antes de qualquer requisição.
|
|
112
|
+
* Uma exceção ao retorno: `client.contacts.export_contacts` devolve **`String`** (o CSV cru),
|
|
113
|
+
porque a rota responde `text/csv` e não JSON.
|
|
91
114
|
|
|
92
115
|
## Mensagens
|
|
93
116
|
|
|
@@ -162,6 +185,48 @@ client.contacts.contacts_check(instance_id: "…", phones: ["+5511999999999"]) #
|
|
|
162
185
|
|
|
163
186
|
O vínculo contato ↔ projeto/número é mantido **automaticamente** pela API.
|
|
164
187
|
|
|
188
|
+
### Importar em lote
|
|
189
|
+
|
|
190
|
+
Até **1000** contatos por chamada, upsert por telefone: o novo entra como
|
|
191
|
+
`pending_validation` (precisa de opt-in antes de campanha), o que já existe só recebe os campos
|
|
192
|
+
informados — valor em branco não apaga o que está lá. Linha ruim vai para `errors` e **não**
|
|
193
|
+
derruba o resto; contato suprimido/opt-out/bloqueado aparece em `skipped_rows` e nunca
|
|
194
|
+
ressuscita. Tags e grupos são criados na hora. `dry_run: true` valida tudo e não grava nada.
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
result = client.contacts.import_contacts(
|
|
198
|
+
contacts: [
|
|
199
|
+
{ phone: "+5511999999999", name: "Ana", email: "ana@example.com", tags: %w[vip] },
|
|
200
|
+
{ phone: "+5511888888888", name: "Bruno", document: "12345678900", document_type: "cpf",
|
|
201
|
+
address: { city: "São Paulo", state: "SP", country: "BR" }, groups: %w[clientes] }
|
|
202
|
+
],
|
|
203
|
+
dry_run: true # ensaio: nada é gravado
|
|
204
|
+
)
|
|
205
|
+
result["created"] # => 2
|
|
206
|
+
result["errors"].each { |row| warn "linha #{row['index']} (#{row['phone']}): #{row['reason']}" }
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Exportar em CSV
|
|
210
|
+
|
|
211
|
+
`export_contacts` aceita os **mesmos filtros** do `list_contacts` (menos `offset`; use `limit`
|
|
212
|
+
para limitar as linhas) e devolve o **texto do CSV** — uma `String` UTF-8, não JSON. Colunas:
|
|
213
|
+
`phone,name,email,status,source,tags,groups,created_at,last_activity_at`, com tags e grupos
|
|
214
|
+
unidos por `;` e instantes em RFC 3339 UTC.
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
require "csv"
|
|
218
|
+
|
|
219
|
+
csv = client.contacts.export_contacts(status: "active", tags: %w[vip], has_email: true,
|
|
220
|
+
created_after: Time.utc(2026, 1, 1), limit: 50_000)
|
|
221
|
+
|
|
222
|
+
File.write("contatos.csv", csv) # gravar como veio
|
|
223
|
+
CSV.parse(csv, headers: true) { |row| puts row["phone"] } # ou percorrer linha a linha
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Vírgulas e aspas dentro dos campos vêm escapadas pelo servidor — não monte o CSV de novo,
|
|
227
|
+
entregue o texto ao parser. Para bases muito grandes, exporte em fatias com os filtros
|
|
228
|
+
(`created_after`/`created_before`, `limit`) em vez de puxar tudo de uma vez.
|
|
229
|
+
|
|
165
230
|
## Campanhas
|
|
166
231
|
|
|
167
232
|
```ruby
|
data/lib/bzapper/codec.rb
CHANGED
|
@@ -99,9 +99,15 @@ module Bzapper
|
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
+
# Corpo cru → texto UTF-8 (bytes inválidos trocados). É o que as rotas de texto
|
|
103
|
+
# (`text/csv`, ex.: `exportContacts`) devolvem, sem passar por JSON.
|
|
104
|
+
def utf8(raw)
|
|
105
|
+
raw.to_s.dup.force_encoding(::Encoding::UTF_8).scrub
|
|
106
|
+
end
|
|
107
|
+
|
|
102
108
|
# Texto UTF-8 (bytes inválidos trocados) → `[json_ou_nil, texto, é_json?]`.
|
|
103
109
|
def decode_json(raw)
|
|
104
|
-
text = raw
|
|
110
|
+
text = utf8(raw)
|
|
105
111
|
return [nil, text, false] if text.strip.empty?
|
|
106
112
|
|
|
107
113
|
[JSON.parse(text), text, true]
|
|
@@ -47,6 +47,33 @@ module Bzapper
|
|
|
47
47
|
timeout: timeout)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# Rotate a tenant API key. Admin only. `POST /keys/{id}/rotate`
|
|
51
|
+
#
|
|
52
|
+
# Creates a NEW key inheriting the old one's role, scopes, project and name, and keeps the OLD
|
|
53
|
+
# key working for a grace period so a running integration does not break mid-deploy. The raw key
|
|
54
|
+
# is shown ONCE. After the deadline the old key answers `401 key_expired`. `revoke_in_seconds:
|
|
55
|
+
# 0` revokes it immediately (default 86400, max 30 days). Partner keys (bZapper Connect) rotate
|
|
56
|
+
# through `/partner/connections/{id}/rotate-key`.
|
|
57
|
+
#
|
|
58
|
+
# @param id [String] API key ID (UUID).
|
|
59
|
+
# @param revoke_in_seconds [Integer, nil] (corpo) Grace period for the OLD key. 0 revokes it
|
|
60
|
+
# immediately.
|
|
61
|
+
# @param idempotency_key [String, nil] chave de idempotência (senão a SDK gera uma).
|
|
62
|
+
# @param timeout [Numeric, nil] segundos por tentativa (padrão: o do cliente).
|
|
63
|
+
# @return [Hash, Array, nil] o JSON da resposta, inteiro (nil em 204).
|
|
64
|
+
# @raise [Bzapper::Error] resposta fora de 2xx ou falha de rede.
|
|
65
|
+
# @raise [ArgumentError] parâmetro de caminho vazio, "." ou "..".
|
|
66
|
+
def rotate_my_key(id, revoke_in_seconds: UNSET, idempotency_key: nil, timeout: nil)
|
|
67
|
+
payload = compact(
|
|
68
|
+
"revoke_in_seconds" => revoke_in_seconds
|
|
69
|
+
)
|
|
70
|
+
request("POST",
|
|
71
|
+
"/keys/#{segment(id, "id")}/rotate",
|
|
72
|
+
body: payload,
|
|
73
|
+
idempotency_key: idempotency_key,
|
|
74
|
+
timeout: timeout)
|
|
75
|
+
end
|
|
76
|
+
|
|
50
77
|
# Authenticated identity (+ profile when it's a user session). `GET /me`
|
|
51
78
|
#
|
|
52
79
|
# @param timeout [Numeric, nil] segundos por tentativa (padrão: o do cliente).
|
|
@@ -27,6 +27,12 @@ module Bzapper
|
|
|
27
27
|
idempotency_key: idempotency_key, timeout: timeout)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# Rotas que respondem TEXTO (`text/csv`, ex.: `exportContacts`): devolve o corpo cru,
|
|
31
|
+
# sem JSON. Mesmos cabeçalhos, novas tentativas e erros do {#request}.
|
|
32
|
+
def request_text(method, path, query: nil, timeout: nil, accept: "text/csv")
|
|
33
|
+
@transport.request(method, path, query: query, timeout: timeout, accept: accept, as_text: true)
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
def segment(value, name)
|
|
31
37
|
Codec.path_segment(value, name)
|
|
32
38
|
end
|
|
@@ -105,6 +105,33 @@ module Bzapper
|
|
|
105
105
|
timeout: timeout)
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
+
# Import contacts in bulk. `POST /contacts/import`
|
|
109
|
+
#
|
|
110
|
+
# Upserts up to 1000 contacts by phone in one call. A new contact is created with `source:
|
|
111
|
+
# import` and `status: pending_validation` (it needs opt-in before a campaign). An existing one
|
|
112
|
+
# has only its informed fields updated — a blank value never erases what is there. A
|
|
113
|
+
# suppressed/opted-out/blocked contact is reported in `skipped_rows` and never resurrected. Tags
|
|
114
|
+
# and groups are created on demand. A bad row is reported in `errors` and does NOT fail the rest
|
|
115
|
+
# of the call. `dry_run` validates everything and writes nothing.
|
|
116
|
+
#
|
|
117
|
+
# @param contacts [Array<Hash>] (corpo)
|
|
118
|
+
# @param dry_run [Boolean, nil] (corpo) Validates and reports without writing anything.
|
|
119
|
+
# @param idempotency_key [String, nil] chave de idempotência (senão a SDK gera uma).
|
|
120
|
+
# @param timeout [Numeric, nil] segundos por tentativa (padrão: o do cliente).
|
|
121
|
+
# @return [Hash, Array, nil] o JSON da resposta, inteiro (nil em 204).
|
|
122
|
+
# @raise [Bzapper::Error] resposta fora de 2xx ou falha de rede.
|
|
123
|
+
def import_contacts(contacts:, dry_run: UNSET, idempotency_key: nil, timeout: nil)
|
|
124
|
+
payload = compact(
|
|
125
|
+
"contacts" => contacts,
|
|
126
|
+
"dry_run" => dry_run
|
|
127
|
+
)
|
|
128
|
+
request("POST",
|
|
129
|
+
"/contacts/import",
|
|
130
|
+
body: payload,
|
|
131
|
+
idempotency_key: idempotency_key,
|
|
132
|
+
timeout: timeout)
|
|
133
|
+
end
|
|
134
|
+
|
|
108
135
|
# Get a contact. `GET /contacts/{id}`
|
|
109
136
|
#
|
|
110
137
|
# @param id [String] Contact ID (UUID).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ESCRITO À MÃO (não sai do script/generate.rb): `exportContacts` responde `text/csv`, e não
|
|
4
|
+
# JSON — está fora dos casos gerados (BRIEF §6, "CSV") e tem teste próprio
|
|
5
|
+
# (test/unit_test.rb, CsvExportTest). O resto do `client.contacts` é gerado
|
|
6
|
+
# (lib/bzapper/resources/contacts.rb).
|
|
7
|
+
|
|
8
|
+
module Bzapper
|
|
9
|
+
module Resources
|
|
10
|
+
class Contacts
|
|
11
|
+
# Export contacts as CSV. `GET /contacts/export`
|
|
12
|
+
#
|
|
13
|
+
# Devolve a base de contatos em **CSV** (`text/csv`, `Content-Disposition: attachment`) —
|
|
14
|
+
# não é JSON. Colunas:
|
|
15
|
+
# `phone,name,email,status,source,tags,groups,created_at,last_activity_at`; tags e grupos
|
|
16
|
+
# vêm unidos por `;` e os instantes em RFC 3339 UTC. Os filtros são os MESMOS do
|
|
17
|
+
# {#list_contacts} (só não há `offset`: use `limit` para limitar as linhas).
|
|
18
|
+
#
|
|
19
|
+
# O retorno é o texto do CSV, como veio do servidor (UTF-8) — passe-o a `CSV.parse` ou
|
|
20
|
+
# grave em disco. Para não carregar tudo na memória, exporte em fatias com os filtros
|
|
21
|
+
# (`created_after`/`created_before`, `limit`).
|
|
22
|
+
#
|
|
23
|
+
# @example Gravar em disco
|
|
24
|
+
# File.write("contatos.csv", client.contacts.export_contacts(status: "active"))
|
|
25
|
+
#
|
|
26
|
+
# @example Ler linha a linha
|
|
27
|
+
# require "csv"
|
|
28
|
+
# CSV.parse(client.contacts.export_contacts(tags: %w[vip]), headers: true) do |row|
|
|
29
|
+
# puts row["phone"]
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# @param search [String, nil] (query) Filter by name, phone, email or document.
|
|
33
|
+
# @param tags [Array<String>, nil] (query) Tag keys (repeat the param or a CSV list).
|
|
34
|
+
# @param tags_match [String, nil] (query) Match ANY (default) or ALL of `tags`.
|
|
35
|
+
# @param groups [Array<String>, nil] (query) Contact-group keys.
|
|
36
|
+
# @param project_id [String, nil] (query) Scope by project (alternative to the X-Project-Id
|
|
37
|
+
# header).
|
|
38
|
+
# @param instance_id [String, nil] (query) Filter by a number (instance) the contact
|
|
39
|
+
# interacted with.
|
|
40
|
+
# @param status [String, nil] (query)
|
|
41
|
+
# @param city [String, nil] (query)
|
|
42
|
+
# @param state [String, nil] (query)
|
|
43
|
+
# @param country [String, nil] (query)
|
|
44
|
+
# @param zip [String, nil] (query)
|
|
45
|
+
# @param document [String, nil] (query)
|
|
46
|
+
# @param has_email [Boolean, nil] (query) Only contacts that have (true) or lack (false) an
|
|
47
|
+
# email.
|
|
48
|
+
# @param last_activity_after [Time, String, nil] (query) last_message_at ≥ this instant
|
|
49
|
+
# (RFC3339).
|
|
50
|
+
# @param last_activity_before [Time, String, nil] (query) last_message_at ≤ this instant
|
|
51
|
+
# (RFC3339).
|
|
52
|
+
# @param created_after [Time, String, nil] (query) created_at ≥ this instant (RFC3339).
|
|
53
|
+
# @param created_before [Time, String, nil] (query) created_at ≤ this instant (RFC3339).
|
|
54
|
+
# @param sort [String, nil] (query)
|
|
55
|
+
# @param limit [Integer, nil] (query) Cap of exported rows.
|
|
56
|
+
# @param timeout [Numeric, nil] segundos por tentativa (padrão: o do cliente).
|
|
57
|
+
# @return [String] o CSV cru (UTF-8), com o cabeçalho na primeira linha.
|
|
58
|
+
# @raise [Bzapper::Error] resposta fora de 2xx ou falha de rede.
|
|
59
|
+
def export_contacts(search: nil, tags: nil, tags_match: nil, groups: nil, project_id: nil,
|
|
60
|
+
instance_id: nil, status: nil, city: nil, state: nil, country: nil,
|
|
61
|
+
zip: nil, document: nil, has_email: nil, last_activity_after: nil,
|
|
62
|
+
last_activity_before: nil, created_after: nil, created_before: nil,
|
|
63
|
+
sort: nil, limit: nil, timeout: nil)
|
|
64
|
+
query = {
|
|
65
|
+
"search" => search,
|
|
66
|
+
"tags" => tags,
|
|
67
|
+
"tags_match" => tags_match,
|
|
68
|
+
"groups" => groups,
|
|
69
|
+
"project_id" => project_id,
|
|
70
|
+
"instance_id" => instance_id,
|
|
71
|
+
"status" => status,
|
|
72
|
+
"city" => city,
|
|
73
|
+
"state" => state,
|
|
74
|
+
"country" => country,
|
|
75
|
+
"zip" => zip,
|
|
76
|
+
"document" => document,
|
|
77
|
+
"has_email" => has_email,
|
|
78
|
+
"last_activity_after" => last_activity_after,
|
|
79
|
+
"last_activity_before" => last_activity_before,
|
|
80
|
+
"created_after" => created_after,
|
|
81
|
+
"created_before" => created_before,
|
|
82
|
+
"sort" => sort,
|
|
83
|
+
"limit" => limit
|
|
84
|
+
}
|
|
85
|
+
request_text("GET", "/contacts/export", query: query, timeout: timeout)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/bzapper/transport.rb
CHANGED
|
@@ -96,8 +96,12 @@ module Bzapper
|
|
|
96
96
|
# Executa a chamada e devolve o JSON decodificado da resposta (`nil` sem corpo).
|
|
97
97
|
#
|
|
98
98
|
# `body: nil` significa SEM corpo JSON; `multipart:` é um {Upload}.
|
|
99
|
+
#
|
|
100
|
+
# `accept:` troca o `Accept` (padrão `application/json`) e `as_text: true` devolve o corpo
|
|
101
|
+
# CRU em texto, sem passar por JSON — é o caminho das rotas `text/csv` (`exportContacts`).
|
|
99
102
|
# @raise [Bzapper::Error]
|
|
100
|
-
def request(method, path, query: nil, body: nil, multipart: nil, idempotency_key: nil, timeout: nil
|
|
103
|
+
def request(method, path, query: nil, body: nil, multipart: nil, idempotency_key: nil, timeout: nil,
|
|
104
|
+
accept: nil, as_text: false)
|
|
101
105
|
method = method.to_s.upcase
|
|
102
106
|
per_try = timeout.nil? ? @timeout : timeout
|
|
103
107
|
unless per_try.is_a?(Numeric) && per_try.positive?
|
|
@@ -108,7 +112,7 @@ module Bzapper
|
|
|
108
112
|
request_id = SecureRandom.uuid.delete("-")
|
|
109
113
|
headers = {
|
|
110
114
|
"Authorization" => "Bearer #{@api_key}",
|
|
111
|
-
"Accept" => "application/json",
|
|
115
|
+
"Accept" => accept || "application/json",
|
|
112
116
|
"X-Bzapper-Client" => CLIENT_ID,
|
|
113
117
|
"User-Agent" => CLIENT_ID,
|
|
114
118
|
# Mesmo id em todas as tentativas desta chamada: é como o suporte correlaciona.
|
|
@@ -146,7 +150,10 @@ module Bzapper
|
|
|
146
150
|
)
|
|
147
151
|
end
|
|
148
152
|
|
|
149
|
-
|
|
153
|
+
if status.between?(200, 299)
|
|
154
|
+
# Texto: o corpo volta como veio (o `INVALID_RESPONSE` do §4 não vale aqui).
|
|
155
|
+
return as_text ? Codec.utf8(raw) : decode(status, resp_headers, raw, request_id)
|
|
156
|
+
end
|
|
150
157
|
|
|
151
158
|
if RETRY_STATUSES.include?(status) && attempt < @max_retries
|
|
152
159
|
@sleeper.call(retry_delay(attempt, resp_headers["retry-after"]))
|
data/lib/bzapper/version.rb
CHANGED
data/lib/bzapper.rb
CHANGED
|
@@ -26,5 +26,7 @@ require_relative "bzapper/codec"
|
|
|
26
26
|
require_relative "bzapper/upload"
|
|
27
27
|
require_relative "bzapper/transport"
|
|
28
28
|
require_relative "bzapper/resources"
|
|
29
|
+
# Escrito à mão: `exportContacts` responde CSV, fora dos métodos gerados.
|
|
30
|
+
require_relative "bzapper/resources/contacts_export"
|
|
29
31
|
require_relative "bzapper/client"
|
|
30
32
|
require_relative "bzapper/webhook"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bzapper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Berni Software
|
|
@@ -31,6 +31,7 @@ files:
|
|
|
31
31
|
- lib/bzapper/resources/campaigns.rb
|
|
32
32
|
- lib/bzapper/resources/connect.rb
|
|
33
33
|
- lib/bzapper/resources/contacts.rb
|
|
34
|
+
- lib/bzapper/resources/contacts_export.rb
|
|
34
35
|
- lib/bzapper/resources/conversations.rb
|
|
35
36
|
- lib/bzapper/resources/groups.rb
|
|
36
37
|
- lib/bzapper/resources/instances.rb
|