Aller au contenu

ARPhone technique

Composants

  • medusa-arphone possède F2, le focus NUI, le shell transparent, les overlays d'appel, le relais HTTP média et le registre applicatif.
  • pma-voice fournit les canaux vocaux 1:1 ; screenshot-basic fournit la capture locale. Leurs révisions sont résolues au build, embarquées puis journalisées au démarrage. Les builders officiels Cfx.re yarn et webpack, requis par le manifeste courant de screenshot-basic, ne sont ni clonés ni vendorisés dans ce dépôt : chaque artefact FXServer embarque déjà ses propres copies sous citizen/system_resources/{yarn,webpack}, que ensure yarn/ensure webpack résolvent automatiquement avant screenshot-basic.
  • FastAPI expose /internal/v1/arphone à FiveM et /api/admin/arphone au web admin. PostgreSQL est autoritaire ; Redis porte limites, typing, grants et états d'appel éphémères.
  • Le volume arphone_media conserve les WebP sous un chemin dérivé de l'UUID serveur.

Modèle de données

La migration 0029_arphone ajoute implants, bindings temporels, réglages, contacts, blocages, appels, conversations, memberships, messages, receipts, photos, attachments, registre/config applicatif et empreintes de numéros retirés. Les bindings actifs sont protégés par indexes uniques partiels.

L'implant est provisionné paresseusement après résolution serveur du personnage actif. La configuration globale du quota est persistée dans l'enregistrement __global__; un override nullable appartient à l'implant.

Médias

La prévisualisation reste locale. Au clic sur Save, la NUI redimensionne dans un canvas au maximum 1280×720, encode en WebP et poste le blob au handler HTTP FXServer avec un grant court. Le handler relaie le binaire à l'API interne. L'API consomme le grant une seule fois, contrôle taille, format, dimensions, quota et checksum, puis écrit par fichier temporaire + renommage atomique. Aucun blob/base64 ne traverse un event réseau FiveM.

Les lectures utilisent aussi des grants courts à usage unique. Une photo masquée reste référençable par ses messages ; une photo mise en quarantaine n'est plus servie au joueur.

Appels et cycle de vie

La machine d'état est ringing -> accepted -> ended, avec sorties declined, busy et missed. L'acceptation configure le même canal pma-voice pour les deux sources. Timeout, déconnexion, changement de personnage et arrêt de ressource remettent les canaux à zéro et persistent la terminaison.

Visibilité NUI et focus

Le document CEF reste plein écran, mais html, body et #app ont un fond explicitement transparent et n'utilisent pas color-scheme, qui peut faire composer un canvas sombre par CEF. La règle [hidden]{display:none!important} garantit qu'une racine inactive ne peint aucun pixel, même si une règle de composant définit son propre mode d'affichage.

app.js dérive toute la visibilité depuis deux états métier : phoneOpen et l'appel courant. syncVisibility() produit trois modes :

  • inactive : racine, panneau et carte d'appel masqués ;
  • phone-open : panneau centre droit visible et interactif ;
  • call-overlay : panneau masqué, seule la carte d'appel est visible.

Les événements d'ouverture, fermeture et transition d'appel passent tous par cette fonction. La temporisation de fin mémorise l'identifiant de l'appel afin de ne pas effacer un nouvel appel arrivé entre-temps. Seul client.lua donne le focus au panneau ouvert ; closePhone() le libère lors de F2, Échap, changement de personnage et arrêt de ressource.

Sécurité

Toutes les routes joueur partent du character_id résolu par medusa-player-utils; l'API recoupe ensuite implant, membership et propriété. Les mutations sensibles sont idempotentes ou contraintes par transaction/index. Les audits utilisent des empreintes opaques et n'enregistrent jamais texte de message, image, token ou identifiant technique caché.

Le shell expose registerApp, unregisterApp, setOverlaySuppressed et getActiveImplantSummary. Une définition de protocole 1 peut fournir son libellé, son badge et son callback/event d'ouverture. La racine NUI reste transparente et non interactive hors du panneau/overlay.

La liste des conversations calcule les non-lus par agrégation SQL dans la page bornée à 50. L'indicateur de saisie est un état Redis de cinq secondes, interrogé toutes les 1,5 seconde uniquement pendant qu'une conversation est ouverte ; aucun polling n'est exécuté par frame.