Перейти к документации
Python

Контакты

`contacts.list`, `get`, `create`, `save`, `update`, `set_audiences`, `delete`, `delete_many`, `list_people`, `set_photo`, `remove_photo`, `block`, `unblock`, `list_threads` и `activity`.

Все методы

usage.py
from openemail import openemail page = openemail.contacts.list(limit=100)contact = openemail.contacts.get('[email protected]') saved = openemail.contacts.create({    'email': '[email protected]',    'name': 'Grace Hopper',    'notes': 'Met at the compiler workshop',}) openemail.contacts.update(saved['email'], {'notes': None})openemail.contacts.set_audiences(saved['email'], {    'audienceIds': ['aud_4c1b8e2a7d9f05c36b4e8a71'],})openemail.contacts.delete(saved['email']) print(len(page['items']), page['hasMore'], contact['source'], contact['lastSeenAt'])

Сначала идут те, кого видели недавно, а контакты, которым никогда не писали, стоят в конце. source равен auto, когда строка появилась потому, что участник отправил на этот адрес сообщение из редактора приложения, а это существенно иное утверждение, нежели то, что кто-то его сохранил. Почта, приходящая с адреса, ничего не записывает, и отправка через этот API тоже.

Книга принадлежит рабочему пространству, а не одному человеку, так что контакт, сохранённый любым участником, видят все участники и все ключи. create записывает source как manual и помещает контакт в аудиторию по умолчанию прямо при записи. Назовите свои списки в audienceIds, чтобы добавить его в них тем же вызовом (для этого нужна ещё и audiences:write), либо добавьте контакт позже через openemail.audiences.add_contact. set_audiences одним вызовом точно задаёт, в каких списках состоит контакт.

Адреса хранятся в нижнем регистре, а клиент кодирует переданный вами, так что [email protected] попадёт в нужную строку. Адрес и есть идентичность, поэтому update не может его изменить: перенос контакта сводится к delete и create.

Параметры: contacts.list

limitint
Сколько контактов возвращать на страницу: целое от 1 до 200, по умолчанию 50. Значение приводится к типу, так что `'100'` из строки запроса подойдёт, а значение вне диапазона даёт 422, а не обрезается до границы.
cursorstr
`nextCursor` с предыдущей страницы. Никогда не собирайте его сами: курсор, называющий контакт, которого больше нет, даёт 400 `invalid_cursor`, а значит ваше состояние постраничности устарело и обход надо начать заново без курсора.
sourceContactSource
`'manual'` выбирает контакты, которые кто-то сохранил намеренно, а `'auto'` выбирает те, что записал редактор приложения. Не указывайте, чтобы получить всю книгу.
qstr
Ищет по имени и адресу, до 200 символов. Если на первой странице ничто не совпадает точно, вместо этого возвращаются близкие написания, и следующие страницы продолжают искать так же.

Ответ: ContactResource

contacts.list возвращает Page[ContactResource], так что строки лежат в page['items'], а обход следует за page['nextCursor'], пока page['hasMore'] равно True, и list_all и iterate делают это за вас. get, create, save, update, set_audiences, set_photo и remove_photo возвращают по одному ContactDetailResource: ту же строку плюс audiences. Адресная книга не ограничена по размеру, поэтому этот маршрут листается постранично, а не возвращает список, который молча обрывается на 200.

objectLiteral['contact']
Всегда строка `contact` как в строках списка, так и в `get`.
emailstr
Адрес, приводимый к нижнему регистру при записи, так что `[email protected]` и `[email protected]` считаются одним контактом, и ключ, который принимает каждый метод контактов, поскольку идентификатор контакта наружу не выдаётся. Строки принадлежат рабочему пространству, а не участнику или ключу, который их записал, так что все участники и все ключи рабочего пространства читают и пишут одну адресную книгу.
namestr | None
`None`, когда для адреса никогда не записывалось имя. Автоматическая запись несёт имя, только если заголовок дал что-то отличное от самого адреса, и она никогда не может перезаписать имя, введённое пользователем.
sourceContactSource | str
`auto` означает, что строка появилась потому, что пользователь отправил почту на этот адрес; `manual` означает, что кто-то ввёл его вручную (существенно иное утверждение), и upsert никогда не понижает `manual` обратно до `auto`. Почта, приходящая с адреса, намеренно не создаёт строки вовсе, так что того, кто вам только писал, здесь нет; объединение остаётся открытым, потому что колонка хранит свободный текст со значением `manual` по умолчанию.
notesstr | None
Свободный текст, который кто-то написал об этом человеке в приложении или через `update`, и никогда не сгенерированный. `None`, когда никто ничего не писал, а явный `None` в `update` очищает его.
lastSeenAtstr | None
ISO-8601 UTC, обновляется каждый раз, когда участник отправляет на этот адрес из редактора приложения, и не обновляется при приходе почты с него, что ничего не записывает. `None` у контакта, сохранённого через `create`, которому никогда не писали, и такие идут в конце убывающего порядка по `lastSeenAt`, который возвращает этот маршрут.
audienceslist[ContactAudienceResource]
Только в `get`, `create`, `save`, `update`, `set_audiences`, `set_photo` и `remove_photo`, никогда в строках списка. Каждая аудитория, в которой состоит контакт, в виде словаря с `id`, `name` и `builtin`, включая аудиторию по умолчанию. `builtin` равен `default` у аудитории, к которой принадлежит каждый контакт, и `None` у созданной кем-то, поэтому ветвитесь по нему, а не по имени, которое может изменить кто угодно.
photoUrlstr | None
Где отдаётся фото контакта, или `None`, если его нет. `set_photo` его задаёт, и каждая загрузка получает новый URL.

Назначение аудиторий контакта

set_audiences(email, {'audienceIds': [...]}) одним запросом точно задаёт, в каких аудиториях состоит контакт. Контакт вступает в каждую перечисленную аудиторию, в которой его ещё нет, и выходит из всех остальных, а вызов возвращает ContactDetailResource после изменения. Требует audiences:write, потому что записывает членство, а не сам контакт, и повтор ничего не меняет.

Аудитория по умолчанию сохраняется всегда, поэтому {'audienceIds': []} оставляет контакт только в аудитории по умолчанию. Принимает до 100 идентификаторов. Идентификатор, не соответствующий ни одной аудитории этого рабочего пространства, даёт 404 audience_not_found, и ничего не меняется, а адрес, не являющийся контактом, даёт 404 contact_not_found.

Все со страницы «Контакты»

list_people перечисляет людей, которых показывает страница «Контакты» в приложении: сохранённые контакты и каждый адрес из почты, у каждого saved, threads и lastAt. list это только сохранённые контакты. Адреса из почты приходят только тогда, когда у ключа есть и threads:read, а page['seen'] сообщает, пришли ли они. sort бывает recent, name или threads, q ищет по именам, адресам и заметкам, а blocked=True оставляет тех, кого блокирует список блокировки рабочего пространства, включая правила на весь домен. blockedBy называет правило в каждой строке.

people.py
from openemail import openemail page = openemail.contacts.list_people(sort='threads', limit=50) for person in page['items']:    if not person['saved'] and (person['threads'] or 0) > 5:        openemail.contacts.save(person['email']) blocked = openemail.contacts.list_all_people(blocked=True)

list_all_people и iterate_people проходят все страницы. Курсор непрозрачен, поэтому возвращайте nextCursor таким, каким он пришёл, с теми же sort, q и blocked.

Сохранение, удаление и фото

save(email, {'name': ..., 'notes': ...}) это «Добавить в контакты» и «Оставить в контактах»: сохраняет адрес, который ещё не контакт, оставляет записанный при отправке как сохранённый вручную и возвращает удалённый. delete это «Удалить»: убирает сохранённый контакт и скрывает адрес, чтобы редактор не записал его снова, и принимает также адрес, который встречался только в почте. wasSaved сообщает, какой это был случай. delete_many удаляет до 200 за один вызов.

photo.py
from pathlib import Path from openemail import openemail openemail.contacts.save('[email protected]', {'name': 'Grace Hopper'}) photo = Path('grace.jpg').read_bytes()contact = openemail.contacts.set_photo('[email protected]', photo, content_type='image/jpeg') openemail.contacts.remove_photo('[email protected]')openemail.contacts.delete_many(['[email protected]', '[email protected]'])

set_photo отправляет байты изображения как есть: PNG, JPEG, WebP или GIF до 5 МБ, вписанные в квадрат 512 пикселей. Передайте content_type=, потому что у байтов нет собственного типа: без него загрузка уходит как application/octet-stream, что отклоняется с 422 invalid_image. Адрес сначала должен быть сохранённым контактом.

Блокировка

block(email) вносит адрес в список блокировки рабочего пространства, чтобы письма с него отклонялись, отбрасывая плюс-метку, а unblock(email) снимает каждое правило, которое его блокирует. Обоим нужен settings:write, потому что они меняют список блокировки, а не контакт, и ни одному не нужно, чтобы адрес был контактом.

Когда unblock снимает правило на весь домен, removed перечисляет его с list, равным blockedDomains, и вместе с ним разблокируются все в этом домене.

Переписка и активность

list_threads(email) постранично проходит цепочки, которые адрес написал или в которых ему писали, во всех папках, а list_all_threads и iterate_threads проходят их целиком. activity(email) возвращает цифры за вкладкой «Активность» контакта: получено и отправлено по интервалам, цепочки, ждущие вашего ответа, и медианное время ответа в каждую сторону. Обоим нужен threads:read.

activity.py
import time from openemail import openemail threads = openemail.contacts.list_threads('[email protected]', q='invoice') activity = openemail.contacts.activity(    '[email protected]',    minutes=30 * 24 * 60,    grain='day',    offset_minutes=time.localtime().tm_gmtoff // 60,) print(len(threads['items']), activity['totals']['waiting'])

Справочник