gRPC-клиент на Python#
Установка#
Готовый пакет для Python не предоставляется. Клиентский код необходимо сгенерировать локально с помощью grpc_tools.protoc, как описано в разделе Генерация gRPC-клиента.
python3 -m venv .venv
.venv/bin/pip install grpcio-tools
.venv/bin/python3 -m grpc_tools.protoc -I proto \
--python_out=gen/python --grpc_python_out=gen/python \
$(find proto -name "*.proto")
Соединение и авторизация#
Для подключения необходимы три параметра:
GREENAPI_GRPC_HOST— адрес gRPC-сервера GREEN-API:grpc.green-api.com:443. Адрес указывается в форматеhost:port, без схемыhttps://;GREENAPI_ID_INSTANCE— значениеidInstanceиз личного кабинета;GREENAPI_API_TOKEN_INSTANCE— значениеapiTokenInstanceиз личного кабинета.
Соединение с gRPC-сервером устанавливается с использованием TLS. Данные авторизации передаются в каждом RPC-вызове отдельно. Подробнее см. раздел Авторизация в gRPC API.
Проверка подключения#
Для проверки подключения можно вызвать метод GetStateInstance, который возвращает текущее состояние инстанса. Если метод успешно выполнился и вернул состояние INSTANCE_STATE_AUTHORIZED, соединение с gRPC-сервером и авторизация настроены корректно, а инстанс готов к работе. Описание других состояний инстанса и возможных ошибок приведено в разделе Проверка подключения обзора gRPC API.
import os
import grpc
from greenapi.instance.v1 import instance_pb2, instance_pb2_grpc
from greenapi.type.v1 import enums_pb2
channel = grpc.secure_channel(
os.environ["GREENAPI_GRPC_HOST"], # "grpc.green-api.com:443"
grpc.ssl_channel_credentials(),
)
client = instance_pb2_grpc.InstanceServiceStub(channel)
metadata = (
("x-instance-id", os.environ["GREENAPI_ID_INSTANCE"]),
("authorization", "Bearer " + os.environ["GREENAPI_API_TOKEN_INSTANCE"]),
)
try:
response = client.GetStateInstance(instance_pb2.GetStateInstanceRequest(), metadata=metadata)
except grpc.RpcError as err:
raise SystemExit(f"{err.code().name}: {err.details()}")
print(enums_pb2.InstanceState.Name(response.state_instance)) # INSTANCE_STATE_AUTHORIZED
Первый вызов#
import os
import grpc
from greenapi.message.v1 import message_pb2, message_pb2_grpc
channel = grpc.secure_channel(
os.environ["GREENAPI_GRPC_HOST"], # "grpc.green-api.com:443"
grpc.ssl_channel_credentials(),
)
client = message_pb2_grpc.MessageServiceStub(channel)
metadata = (
("x-instance-id", os.environ["GREENAPI_ID_INSTANCE"]),
("authorization", "Bearer " + os.environ["GREENAPI_API_TOKEN_INSTANCE"]),
)
response = client.SendMessage(
message_pb2.SendMessageRequest(
chat_id="11001234567@c.us",
message="привет из gRPC",
),
metadata=metadata,
)
print(response.id_message)
Одно gRPC-соединение можно использовать для всех сервисов. Клиентские Stub остальных сервисов создаются на основе того же channel.
Как загрузить файл#
Файл передаётся с помощью клиентского потока. Первое сообщение UploadFileRequest содержит UploadFileMetadata с именем файла и типом содержимого. Затем клиент последовательно передаёт сообщения с chunk, содержащими части файла.
После завершения итератора запросов gRPC завершает отправку клиентского потока (half-close) и ожидает единственный ответ UploadFileResponse от сервера.
Общий контракт потоковой загрузки и требования к размеру чанков описаны в разделе Потоковые вызовы gRPC API.
В Python клиентский поток передаётся в виде итератора. Когда итератор завершается, gRPC прекращает отправку сообщений серверу и ожидает единственный ответ.
def upload_file(client, metadata, path, content_type, chunk_size=64 * 1024):
def requests():
yield message_pb2.UploadFileRequest(
metadata=message_pb2.UploadFileMetadata(
file_name=os.path.basename(path),
content_type=content_type,
)
)
with open(path, "rb") as f:
while chunk := f.read(chunk_size):
yield message_pb2.UploadFileRequest(chunk=chunk)
response = client.UploadFile(requests(), metadata=metadata)
return response.url_file
url = upload_file(client, metadata, "/path/to/photo.jpg", "image/jpeg")
Как получить QR-код#
Метод ScanQrCode использует серверный поток: сервер последовательно отправляет ответы с QR-кодами до завершения попытки авторизации. Статусы и поведение метода описаны в разделе Получение QR-кода по gRPC.
try:
for resp in client.ScanQrCode(instance_pb2.ScanQrCodeRequest(), metadata=metadata):
if resp.status == instance_pb2.QR_CODE_STATUS_QR_CODE:
show_qr("data:image/png;base64," + base64.b64encode(resp.qr_png).decode())
elif resp.status == instance_pb2.QR_CODE_STATUS_ALREADY_LOGGED:
raise RuntimeError("инстанс уже авторизован, нужен Logout")
except grpc.RpcError as err:
# DEADLINE_EXCEEDED — никто не отсканировал; FAILED_PRECONDITION — инстанс не готов
print(err.code(), err.details())
Когда сервер завершает поток, итератор исчерпывается и цикл for завершается автоматически.
Обработка ошибок#
Статус gRPC можно получить с помощью err.code(), а описание ошибки — с помощью err.details():
try:
response = client.SendMessage(request, metadata=metadata)
except grpc.RpcError as err:
print(err.code(), err.details())
Описание возможных статусов gRPC и рекомендации по повторным вызовам приведены в разделе Коды ошибок gRPC API.
Перед автоматическим повтором методов отправки ознакомьтесь с разделом Повторная отправка сообщений: повторный вызов может привести к повторной отправке одного и того же сообщения.