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
96 changes: 89 additions & 7 deletions client/api_patches/src/controller/callController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
type CallActionPayload = {
callId?: string;
to?: string;
groupJid?: string;
isVideo?: boolean;
};

Expand Down Expand Up @@ -478,7 +479,56 @@ async function evaluateWppCall(req: Request, action: string, payload: CallAction
return null;
};

if (action === 'offer') {
// A group call has no single peer to dial. WhatsApp's own group-info
// call buttons go through WAWebVoipStartCall.startWAWebVoipGroupCallFromChat
// with the CALL_FROM_UI.GROUP_CHAT_INFO / LOBBY_ENTRY_POINT_TYPE.GROUP_CHAT_INFO
// entry points: it preloads the group-call bundle, opens the lobby and
// invites the group. The resulting call then lives in CallStore exactly
// like a one-to-one one, which is why the polling below is shared.
// Never hand-roll group signaling here.
const startOutgoingGroupCall = async (
groupJid: string,
isVideo: boolean
): Promise<any> => {
const requireWa = (name: string): any => {
try {
return win.require?.(name);
} catch (_) {
return null;
}
};
const start =
win.WPP?.whatsapp?.functions?.startWAWebVoipGroupCallFromChat ||
requireWa('WAWebVoipStartCall')?.startWAWebVoipGroupCallFromChat;
if (typeof start !== 'function') {
throw new Error('startWAWebVoipGroupCallFromChat is not available');
}
const createWid = requireWa('WAWebWidFactory')?.createWid;
const chats = requireWa('WAWebChatCollection')?.ChatCollection;
if (typeof createWid !== 'function' || typeof chats?.get !== 'function') {
throw new Error('WhatsApp chat collection is not available');
}
const chat = chats.get(createWid(groupJid));
if (!chat || !chat.groupMetadata) {
throw new Error(`The group ${groupJid} is not a callable group chat`);
}
const callFromUi =
requireWa('WAWebWamEnumCallFromUi')?.CALL_FROM_UI?.GROUP_CHAT_INFO ?? 10;
const lobbyEntryPoint =
requireWa('WAWebWamEnumLobbyEntryPointType')?.LOBBY_ENTRY_POINT_TYPE
?.GROUP_CHAT_INFO ?? 11;
await start(chat, isVideo, callFromUi, lobbyEntryPoint);
return null;
};

// Current WhatsApp Web promotes the real ongoing call through
// CallStore.activeCall. The value returned by the legacy collection
// lookup may point at an older call model, so prefer a newly-active call
// and only trust a direct return value when it is not stale.
const waitForOfferedCall = async (
place: () => Promise<any>,
kind: string
): Promise<any> => {
let call: any = null;
const storeBeforeOffer = getCallStore();
const preexistingIds = new Set(
Expand All @@ -488,12 +538,8 @@ async function evaluateWppCall(req: Request, action: string, payload: CallAction

for (let attempt = 0; attempt < 3 && !call; attempt += 1) {
await ensureVoipRuntimeReady();
const offered = await startOutgoingCall(String(payload.to), !!payload.isVideo);
const offered = await place();

// Current WhatsApp Web promotes the real ongoing call through
// CallStore.activeCall. The value returned by the legacy collection
// lookup may point at an older call model, so prefer a newly-active
// call and only trust the direct return value when it is not stale.
for (let waitAttempt = 0; waitAttempt < 50 && !call; waitAttempt += 1) {
const store = getCallStore();
const active = store?.activeCall;
Expand Down Expand Up @@ -525,7 +571,23 @@ async function evaluateWppCall(req: Request, action: string, payload: CallAction
}
if (!call && attempt < 2) await delay(300 * (attempt + 1));
}
if (!call) throw new Error('WhatsApp did not create an outgoing call model');
if (!call) throw new Error(`WhatsApp did not create ${kind} call model`);
return call;
};

if (action === 'offer') {
const call = await waitForOfferedCall(
() => startOutgoingCall(String(payload.to), !!payload.isVideo),
'an outgoing'
);
return summarizeCall(call);
}

if (action === 'group-offer') {
const call = await waitForOfferedCall(
() => startOutgoingGroupCall(String(payload.groupJid), !!payload.isVideo),
'an outgoing group'
);
return summarizeCall(call);
}

Expand Down Expand Up @@ -656,6 +718,26 @@ export async function offerCall(req: Request, res: Response) {
}
}

export async function offerGroupCall(req: Request, res: Response) {
try {
const body = req.body || {};
if (!body.groupJid) {
res.status(400).json({ status: 'error', message: 'Parameter groupJid is required!' });
return;
}
await prepareAudioBridge(req);
const stopWatching = watchOutgoingCallNotices(req);
try {
ok(res, await evaluateWppCall(req, 'group-offer', body));
} finally {
stopWatching();
}
} catch (error) {
await stopAudioBridge(req);
fail(req, res, 'offerGroupCall', error);
}
}

/**
* While an outgoing call is being placed, WhatsApp Web can open a notice the
* hidden page has no one to answer -- seen live calling a WhatsApp Business
Expand Down
6 changes: 6 additions & 0 deletions client/api_patches/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,12 @@ routes.post(
statusConnection,
CallController.offerCall
);
routes.post(
'/api/:session/call/group/offer',
verifyToken,
statusConnection,
CallController.offerGroupCall
);
routes.get(
'/api/:session/call/diagnostics',
verifyToken,
Expand Down
4 changes: 4 additions & 0 deletions client/changelog_en-US.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This release brings communities and a reorganized settings window, on top of eve

WHAT'S NEW

- Group calls: the voice and video call buttons now appear in a group conversation and start a call to the group, and an incoming group call rings, is announced by the group's name and can be answered, with the same Answer button and global answer key as a one-to-one call. Turning the camera on during a voice call works as it does in a one-to-one call, and call events from each participant stay with the group, so the call window keeps the group's name from the first ring to the end.

- Video calls: WinZapp now finds the camera with the FFmpeg build it ships, which lists DirectShow devices without the "DirectShow video devices" heading. That build returned no camera at all, so a video call went out receive-only, with "No camera found" in the log.

- Buttons in business messages can be answered: a reply button sends the answer the way WhatsApp Web does (reply buttons of plain messages, quick replies of interactive messages, template buttons and the rows of lists), a link button opens the link, a copy button copies the code and a call button says the number. The buttons appear under the message list when the message is selected.

- WPPConnect Server 2.10.61 is now offered as an update without waiting for a new WinZapp version. It only updates dependencies, and WinZapp's changes to the server are not affected.
Expand Down
4 changes: 4 additions & 0 deletions client/changelog_es-ES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Esta versión trae las comunidades y una ventana de configuración reorganizada,

NOVEDADES

- Llamadas grupales: los botones de llamada de voz y de vídeo aparecen ahora en la conversación del grupo e inician una llamada al grupo, y una llamada grupal entrante suena, se anuncia con el nombre del grupo y se puede contestar, con el mismo botón de contestar y el mismo atajo global de contestar que una llamada individual. Activar la cámara durante una llamada de voz funciona como en una llamada individual, y los eventos de llamada de cada participante quedan con el grupo, así que la ventana de la llamada mantiene el nombre del grupo desde el primer tono hasta el final.

- Videollamadas: WinZapp ahora encuentra la cámara con el FFmpeg incluido en WinZapp, que enumera los dispositivos DirectShow sin el encabezado "DirectShow video devices". Esa versión de FFmpeg no devolvía ninguna cámara, así que la videollamada salía solo recibiendo, con "No camera found" en el registro.

- Ya se pueden usar los botones de los mensajes de empresas: un botón de respuesta envía la respuesta igual que WhatsApp Web (botones de respuesta de mensajes simples, respuestas rápidas de mensajes interactivos, botones de plantilla y filas de listas), un botón de enlace abre el enlace, un botón de copiar copia el código y un botón de llamada dice el número. Los botones aparecen debajo de la lista de mensajes cuando el mensaje está seleccionado.

- WPPConnect Server 2.10.61 ya se ofrece como actualización sin esperar a una nueva versión de WinZapp. Solo actualiza dependencias y no afecta a los cambios que WinZapp hace en el servidor.
Expand Down
4 changes: 4 additions & 0 deletions client/changelog_pl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ W tej wersji pojawiają się społeczności i zreorganizowane okno ustawień, op

NOWOŚCI

- Połączenia grupowe: przyciski połączenia głosowego i wideo pojawiają się teraz w rozmowie grupowej i rozpoczynają połączenie z grupą, a przychodzące połączenie grupowe dzwoni, jest zapowiadane nazwą grupy i można je odebrać tym samym przyciskiem odbierania i tym samym globalnym skrótem odbierania co połączenie indywidualne. Włączenie kamery podczas połączenia głosowego działa jak w połączeniu indywidualnym, a zdarzenia połączenia od każdego uczestnika pozostają przy grupie, więc okno połączenia zachowuje nazwę grupy od pierwszego dzwonka do końca.

- Połączenia wideo: WinZapp znajduje teraz kamerę za pomocą dołączonego FFmpeg, który wyświetla urządzenia DirectShow bez nagłówka „DirectShow video devices”. Ta wersja FFmpeg nie zwracała żadnej kamery, więc połączenie wideo było tylko odbiorcze, z „No camera found” w logu.

- Przyciski w wiadomościach firm można już używać: przycisk odpowiedzi wysyła odpowiedź tak samo jak WhatsApp Web (przyciski odpowiedzi zwykłych wiadomości, szybkie odpowiedzi wiadomości interaktywnych, przyciski szablonów i wiersze list), przycisk linku otwiera link, przycisk kopiowania kopiuje kod, a przycisk połączenia odczytuje numer. Przyciski pojawiają się pod listą wiadomości, gdy wiadomość jest zaznaczona.

- WPPConnect Server 2.10.61 jest teraz oferowany jako aktualizacja bez czekania na nową wersję WinZapp. Aktualizuje tylko zależności i nie wpływa na zmiany, które WinZapp wprowadza w serwerze.
Expand Down
4 changes: 4 additions & 0 deletions client/changelog_pt-BR.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Esta versão traz as comunidades e uma janela de configurações reorganizada, a

NOVIDADES

- Chamadas em grupo: os botões de chamada de voz e de vídeo agora aparecem na conversa do grupo e iniciam uma chamada para o grupo, e uma chamada em grupo recebida toca, é anunciada pelo nome do grupo e pode ser atendida, com o mesmo botão de atender e o mesmo atalho global de atender de uma chamada individual. Ligar a câmera durante uma chamada de voz funciona como numa chamada individual, e os eventos de chamada de cada participante ficam com o grupo, então a janela da chamada mantém o nome do grupo do primeiro toque até o fim.

- Chamadas de vídeo: o WinZapp agora encontra a câmera com o FFmpeg incluído no WinZapp, que lista os dispositivos DirectShow sem o cabeçalho "DirectShow video devices". Essa versão do FFmpeg não retornava câmera nenhuma, então a chamada de vídeo saía só recebendo, com "No camera found" no log.

- Os botões das mensagens de empresas agora podem ser usados: um botão de resposta envia a resposta do mesmo jeito que o WhatsApp Web (botões de resposta de mensagens simples, respostas rápidas de mensagens interativas, botões de modelo e linhas de listas), um botão de link abre o link, um botão de copiar copia o código e um botão de ligar diz o número. Os botões aparecem abaixo da lista de mensagens quando a mensagem está selecionada.

- O WPPConnect Server 2.10.61 agora é oferecido como atualização sem esperar por uma nova versão do WinZapp. Ele só atualiza dependências e não afeta as alterações que o WinZapp faz no servidor.
Expand Down
4 changes: 4 additions & 0 deletions client/changelog_pt-PT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Esta versão traz as comunidades e uma janela de definições reorganizada, alé

NOVIDADES

- Chamadas de grupo: os botões de chamada de voz e de vídeo aparecem agora na conversa do grupo e iniciam uma chamada para o grupo, e uma chamada de grupo recebida toca, é anunciada pelo nome do grupo e pode ser atendida, com o mesmo botão de atender e o mesmo atalho global de atender de uma chamada individual. Ligar a câmara durante uma chamada de voz funciona como numa chamada individual, e os eventos de chamada de cada participante ficam com o grupo, pelo que a janela da chamada mantém o nome do grupo desde o primeiro toque até ao fim.

- Chamadas de vídeo: o WinZapp encontra agora a câmara com o FFmpeg incluído no WinZapp, que lista os dispositivos DirectShow sem o cabeçalho "DirectShow video devices". Essa versão do FFmpeg não devolvia câmara nenhuma, pelo que a chamada de vídeo seguia apenas a receber, com "No camera found" no registo.

- Os botões das mensagens de empresas passam a poder ser usados: um botão de resposta envia a resposta da mesma forma que o WhatsApp Web (botões de resposta de mensagens simples, respostas rápidas de mensagens interativas, botões de modelo e linhas de listas), um botão de ligação abre a ligação, um botão de copiar copia o código e um botão de chamada diz o número. Os botões aparecem por baixo da lista de mensagens quando a mensagem está selecionada.

- O WPPConnect Server 2.10.61 passa a ser oferecido como atualização sem esperar por uma nova versão do WinZapp. Só atualiza dependências e não afeta as alterações que o WinZapp faz no servidor.
Expand Down
4 changes: 4 additions & 0 deletions client/changelog_ro.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Această versiune aduce comunitățile și o fereastră de setări reorganizată

NOUTĂȚI

- Apeluri de grup: butoanele de apel vocal și video apar acum în conversația de grup și pornesc un apel către grup, iar un apel de grup primit sună, este anunțat cu numele grupului și poate fi răspuns, cu același buton de răspuns și aceeași comandă rapidă globală de răspuns ca un apel individual. Pornirea camerei în timpul unui apel vocal funcționează ca într-un apel individual, iar evenimentele de apel de la fiecare participant rămân la grup, așa că fereastra apelului păstrează numele grupului de la primul sunet până la final.

- Apeluri video: WinZapp găsește acum camera cu FFmpeg-ul inclus în WinZapp, care listează dispozitivele DirectShow fără antetul „DirectShow video devices”. Acea versiune de FFmpeg nu returna nicio cameră, așa că apelul video ieșea doar cu recepție, cu „No camera found” în log.

- Butoanele din mesajele firmelor pot fi acum folosite: un buton de răspuns trimite răspunsul la fel ca WhatsApp Web (butoanele de răspuns ale mesajelor simple, răspunsurile rapide ale mesajelor interactive, butoanele de șablon și rândurile listelor), un buton de link deschide linkul, un buton de copiere copiază codul, iar un buton de apel spune numărul. Butoanele apar sub lista de mesaje când mesajul este selectat.

- WPPConnect Server 2.10.61 este acum oferit ca actualizare, fără a aștepta o nouă versiune WinZapp. Actualizează doar dependențe și nu afectează modificările pe care WinZapp le face în server.
Expand Down
4 changes: 4 additions & 0 deletions client/changelog_tr-TR.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Bu sürüm, 2.1.0.0 sürümündeki her şeye ek olarak toplulukları ve yeniden

YENİLİKLER

- Grup aramaları: sesli ve görüntülü arama düğmeleri artık grup sohbetinde görünür ve gruba arama başlatır; gelen bir grup araması çalar, grubun adıyla duyurulur ve tek kişilik aramadaki aynı Yanıtla düğmesi ve genel yanıtlama kısayoluyla yanıtlanabilir. Sesli arama sırasında kamerayı açmak tek kişilik aramadaki gibi çalışır ve her katılımcıdan gelen arama olayları grupta kalır, böylece arama penceresi ilk çalmadan sona kadar grubun adını korur.

- Görüntülü aramalar: WinZapp artık kamerayı, DirectShow aygıtlarını "DirectShow video devices" başlığı olmadan listeleyen ve WinZapp ile birlikte gelen FFmpeg ile bulur. Bu FFmpeg sürümü hiçbir kamera döndürmüyordu, bu yüzden görüntülü arama yalnızca alım modunda başlıyordu ve günlükte "No camera found" yazıyordu.

- İşletme mesajlarındaki düğmeler artık kullanılabiliyor: bir yanıt düğmesi yanıtı WhatsApp Web ile aynı şekilde gönderir (düz mesajların yanıt düğmeleri, etkileşimli mesajların hızlı yanıtları, şablon düğmeleri ve listelerin satırları), bağlantı düğmesi bağlantıyı açar, kopyalama düğmesi kodu kopyalar, arama düğmesi numarayı söyler. Mesaj seçildiğinde düğmeler mesaj listesinin altında görünür.

- WPPConnect Server 2.10.61 artık yeni bir WinZapp sürümü beklenmeden güncelleme olarak sunuluyor. Yalnızca bağımlılıkları günceller ve WinZapp'ın sunucuda yaptığı değişiklikleri etkilemez.
Expand Down
16 changes: 11 additions & 5 deletions client/core/call_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@


def incoming_call_can_answer(details: dict | None) -> bool:
"""Return whether this one-to-one incoming call can be answered."""
return not bool((details or {}).get("is_group"))
"""Return whether this incoming call can be answered.

Group offers are answerable: WhatsApp Web accepts a group call through the
same native ``voipStack.acceptCall`` its own Answer button uses, and
WinZapp attaches Python audio to the resulting call. This stays the single
decision point for the alert UI and the global answer key.
"""
return True


def active_call_label_key(active: dict | None) -> str:
"""Return the label for an active individual call."""
"""Return the label for an active call, group calls included."""
return "video_call_active_label" if (active or {}).get("is_video") else "voice_call_active_label"


Expand Down Expand Up @@ -79,8 +85,8 @@ def call_hotkey_setting(general, key):
def first_answerable_call(active_calls, details_map) -> str:
"""Return the identity of the oldest ringing call that can be answered.

A group offer rings too, but cannot be answered, so it must not be the
call a global "answer" key acts on when a one-to-one call rings beside it.
Group offers ring and are answerable too, so they take part in the same
oldest-first order as one-to-one calls rather than being skipped.
"""
for identity in active_calls or ():
if incoming_call_can_answer((details_map or {}).get(identity)):
Expand Down
Loading
Loading