Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,10 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
python -m pip install ruff pytest pytest-asyncio ".[voip]"
Comment thread
AshLeigh-Fraxinus marked this conversation as resolved.
- name: Lint with ruff
run: |
# stop the build if there are Python syntax errors or undefined names
ruff check --output-format=github --select=E9,F63,F7,F82 --target-version=py37 .
# default set of ruff rules with GitHub Annotations
ruff check --output-format=github --target-version=py37 .
ruff check --output-format=github --select=E4,E7,E9,F --target-version=py310 .
- name: Test with pytest
run: |
pytest
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,4 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
/.idea/
Comment thread
kennyromanov marked this conversation as resolved.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ authorize the account, go to your [cabinet](https://console.green-api.com/) and
python -m pip install whatsapp-api-client-python
```

VoIP calls require the optional dependencies:

```shell
python -m pip install "whatsapp-api-client-python[voip]"
```

## Import

```
Expand Down Expand Up @@ -302,6 +308,8 @@ asyncio.run(main())
| Example of account methods asynchronously | [accountMethodsAsync.py](./examples/async/accountMethodsAsync.py) |
| Example of getting last incoming and outgoing calls | [lastCalls.py](./examples/sync/lastCalls.py) |
| Example of getting last calls asynchronously | [lastCallsAsync.py](./examples/async/lastCallsAsync.py) |
| Outgoing headless VoIP call | [headless_call.py](./examples/async/voip/headless_call.py) |
| Incoming headless VoIP call | [incoming_call.py](./examples/async/voip/incoming_call.py) |
| Example of sending a message with link preview options | [sendMessageWithPreview.py](./examples/sync/sending/sendMessageWithPreview.py) |
| Example of sending a message with link preview options asynchronously | [sendMessageWithPreviewAsync.py](./examples/async/sending/sendMessageWithPreviewAsync.py) |
| Example of queues methods (counts, clear webhooks queue) | [queuesMethods.py](./examples/sync/queuesMethods.py) |
Expand Down
8 changes: 8 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ whatsapp-api-client-python - библиотека для интеграции с
python -m pip install whatsapp-api-client-python
```

Для VoIP нужны дополнительные зависимости:

```shell
python -m pip install "whatsapp-api-client-python[voip]"
```

## Импорт

```
Expand Down Expand Up @@ -303,6 +309,8 @@ asyncio.run(main())
| Пример асинхронных методов аккаунта | [accountMethodsAsync.py](../examples/async/accountMethodsAsync.py) |
| Пример получения журнала звонков | [lastCalls.py](../examples/sync/lastCalls.py) |
| Пример асинхронного получения журнала звонков | [lastCallsAsync.py](../examples/async/lastCallsAsync.py) |
| Исходящий VoIP-звонок без аудиоустройств | [headless_call.py](../examples/async/voip/headless_call.py) |
| Входящий VoIP-звонок без аудиоустройств | [incoming_call.py](../examples/async/voip/incoming_call.py) |
| Пример отправки сообщения с настройками превью ссылки | [sendMessageWithPreview.py](../examples/sync/sending/sendMessageWithPreview.py) |
| Пример асинхронной отправки сообщения с превью ссылки | [sendMessageWithPreviewAsync.py](../examples/async/sending/sendMessageWithPreviewAsync.py) |
| Пример методов очереди (счётчики, очистка вебхуков) | [queuesMethods.py](../examples/sync/queuesMethods.py) |
Expand Down
58 changes: 58 additions & 0 deletions examples/async/voip/headless_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Outgoing voice call using a WAV file and decoded audio frames, no audio devices."""

import asyncio
import os
import sys

from aiortc.contrib.media import MediaPlayer

from whatsapp_api_client_python.API import GreenAPI
from whatsapp_api_client_python.tools.voip import CallAudio, FrameAudioSink


async def make_audio() -> CallAudio:
player = MediaPlayer(sys.argv[2])

if player.audio is None:
raise ValueError("The file has no audio stream")

async def on_frame(frame):
# Replace this with your application's frame consumer.
print(f"received {frame.samples} samples")

sink = FrameAudioSink(on_frame)

async def close():
await sink.closeAsync()
player.audio.stop()

return CallAudio(player.audio, sink.attachAsync, close)


async def main():
api = GreenAPI(os.environ["GREEN_API_ID"], os.environ["GREEN_API_TOKEN"])
calls = api.voip.connect(audio_factory=make_audio)
ended = asyncio.Event()
calls.on("incoming_call", lambda info: print("Incoming call:", info.wid))

def on_end(detail):
print("Call ended:", detail)
ended.set()

calls.on("end_call", on_end)
calls.on("error", lambda detail: print("Call error:", detail))

try:
await calls.openAsync(timeout=30)
await api.voip.dialAsync(sys.argv[1])
await calls.startAudioAsync()
await ended.wait()
finally:
await calls.closeAsync()


if __name__ == "__main__":
if len(sys.argv) != 3:
raise SystemExit("Usage: headless_call.py <phone> <audio.wav>")

asyncio.run(main())
63 changes: 63 additions & 0 deletions examples/async/voip/incoming_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Answer an incoming voice call using a WAV file, without audio devices."""

import asyncio
import os
import sys

from aiortc.contrib.media import MediaPlayer

from whatsapp_api_client_python.API import GreenAPI
from whatsapp_api_client_python.tools.voip import CallAudio, FrameAudioSink


async def make_audio() -> CallAudio:
player = MediaPlayer(sys.argv[1])

if player.audio is None:
raise ValueError("The file has no audio stream")

async def on_frame(frame):
# Replace this with your application's frame consumer.
print(f"received {frame.samples} samples")

sink = FrameAudioSink(on_frame)

async def close():
await sink.closeAsync()
player.audio.stop()

return CallAudio(player.audio, sink.attachAsync, close)


async def main():
api = GreenAPI(os.environ["GREEN_API_ID"], os.environ["GREEN_API_TOKEN"])
calls = api.voip.connect(audio_factory=make_audio)
incoming = asyncio.Queue()
ended = asyncio.Event()

calls.on("incoming_call", incoming.put_nowait)
calls.on("end_call", lambda detail: ended.set())
calls.on("error", lambda detail: print("Call error:", detail))

try:
await calls.openAsync(timeout=30)

info = await incoming.get()

print("Incoming call:", info.wid)

if calls.state is None or calls.state.state != "inc-call":
return

await api.voip.acceptAsync()
await calls.startAudioAsync()
await ended.wait()
finally:
await calls.closeAsync()


if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("Usage: incoming_call.py <audio.wav>")

asyncio.run(main())
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="whatsapp-api-client-python",
version="0.0.54",
version="0.0.55",
description=(
"This library helps you easily create"
" a Python application with WhatsApp API."
Expand Down Expand Up @@ -40,6 +40,7 @@
"Creative Commons Attribution-NoDerivatives 4.0 International"
" (CC BY-ND 4.0)"
),
install_requires=["requests-2.34.2", "aiofiles>=24.1.0", "aiogram>=3.28.2", "aiohttp>=3.13.5"],
install_requires=["requests==2.34.2", "aiofiles>=24.1.0", "aiogram>=3.28.2", "aiohttp>=3.13.5"],
extras_require={"voip": ["aiortc>=1.15,<2", "websockets>=16,<17"]},
python_requires=">=3.10"
)
Loading
Loading