# GCPhone GCPhone est une application téléphone complète et personnalisable pour les serveurs FiveM (modification multijoueur de Grand Theft Auto V). Ce projet permet aux joueurs de communiquer via SMS, appels vocaux (incluant WebRTC), et fournit des applications intégrées comme Twitter, banque, bourse, et chat anonyme. L'architecture est basée sur Vue.js pour l'interface utilisateur et Lua pour l'intégration avec le framework ESX de FiveM. Le téléphone offre une expérience réaliste avec gestion des contacts, historique d'appels, postes téléphoniques fixes, appels anonymes, et intégration avec les métiers ESX. Le système utilise MySQL pour la persistance des données et supporte les appels WebRTC pour une communication vocale de haute qualité entre joueurs. ## Événements Serveur - Gestion des Contacts Les événements serveur permettent d'ajouter, modifier et supprimer des contacts stockés dans la base de données MySQL. ```lua -- Ajouter un nouveau contact RegisterServerEvent('gcPhone:addContact') AddEventHandler('gcPhone:addContact', function(display, phoneNumber) local sourcePlayer = tonumber(source) local xplayer = ESX.GetPlayerFromId(source) local identifier = xplayer.identifier addContact(sourcePlayer, identifier, phoneNumber, display) end) -- Mettre à jour un contact existant RegisterServerEvent('gcPhone:updateContact') AddEventHandler('gcPhone:updateContact', function(id, display, phoneNumber) local sourcePlayer = tonumber(source) local xplayer = ESX.GetPlayerFromId(source) local identifier = xplayer.identifier updateContact(sourcePlayer, identifier, id, phoneNumber, display) end) -- Supprimer un contact RegisterServerEvent('gcPhone:deleteContact') AddEventHandler('gcPhone:deleteContact', function(id) local sourcePlayer = tonumber(source) local xplayer = ESX.GetPlayerFromId(source) local identifier = xplayer.identifier deleteContact(sourcePlayer, identifier, id) end) ``` ## Événements Serveur - Messagerie SMS Système complet de messagerie permettant l'envoi, la réception et la gestion des SMS entre joueurs. ```lua -- Envoyer un message SMS RegisterServerEvent('gcPhone:sendMessage') AddEventHandler('gcPhone:sendMessage', function(phoneNumber, message) local sourcePlayer = tonumber(source) local xplayer = ESX.GetPlayerFromId(source) local identifier = xplayer.identifier addMessage(sourcePlayer, identifier, phoneNumber, message) end) -- Supprimer un message spécifique RegisterServerEvent('gcPhone:deleteMessage') AddEventHandler('gcPhone:deleteMessage', function(msgId) deleteMessage(msgId) end) -- Supprimer tous les messages d'un numéro RegisterServerEvent('gcPhone:deleteMessageNumber') AddEventHandler('gcPhone:deleteMessageNumber', function(number) local sourcePlayer = tonumber(source) local xplayer = ESX.GetPlayerFromId(source) local identifier = xplayer.identifier deleteAllMessageFromPhoneNumber(sourcePlayer, identifier, number) end) -- Marquer les messages comme lus RegisterServerEvent('gcPhone:setReadMessageNumber') AddEventHandler('gcPhone:setReadMessageNumber', function(num) local xplayer = ESX.GetPlayerFromId(source) local identifier = xplayer.identifier setReadMessageNumber(identifier, num) end) ``` ## Événements Serveur - Système d'Appels Gestion complète des appels vocaux avec support WebRTC et appels vers postes fixes. ```lua -- Démarrer un appel (avec support WebRTC optionnel) RegisterServerEvent('gcPhone:startCall') AddEventHandler('gcPhone:startCall', function(phone_number, rtcOffer, extraData) TriggerEvent('gcPhone:internal_startCall', source, phone_number, rtcOffer, extraData) end) -- Accepter un appel entrant RegisterServerEvent('gcPhone:acceptCall') AddEventHandler('gcPhone:acceptCall', function(infoCall, rtcAnswer) local id = infoCall.id if AppelsEnCours[id] ~= nil then AppelsEnCours[id].is_accepts = true AppelsEnCours[id].rtcAnswer = rtcAnswer TriggerClientEvent('gcPhone:acceptCall', AppelsEnCours[id].transmitter_src, AppelsEnCours[id], true) SetTimeout(1000, function() TriggerClientEvent('gcPhone:acceptCall', AppelsEnCours[id].receiver_src, AppelsEnCours[id], false) end) saveAppels(AppelsEnCours[id]) end end) -- Rejeter un appel RegisterServerEvent('gcPhone:rejectCall') AddEventHandler('gcPhone:rejectCall', function(infoCall) local id = infoCall.id if AppelsEnCours[id] ~= nil then TriggerClientEvent('gcPhone:rejectCall', AppelsEnCours[id].transmitter_src) TriggerClientEvent('gcPhone:rejectCall', AppelsEnCours[id].receiver_src) AppelsEnCours[id] = nil end end) ``` ## Événements Client - Interface NUI Callbacks NUI permettant la communication entre l'interface Vue.js et le client Lua. ```lua -- Envoyer un SMS depuis l'interface RegisterNUICallback('sendMessage', function(data, cb) if data.message == '%pos%' then local myPos = GetEntityCoords(PlayerPedId()) data.message = 'GPS: ' .. myPos.x .. ', ' .. myPos.y end TriggerServerEvent('gcPhone:sendMessage', data.phoneNumber, data.message) end) -- Démarrer un appel depuis l'interface RegisterNUICallback('startCall', function(data, cb) startCall(data.numero, data.rtcOffer, data.extraData) cb() end) -- Accepter un appel depuis l'interface RegisterNUICallback('acceptCall', function(data, cb) acceptCall(data.infoCall, data.rtcAnswer) cb() end) -- Définir un point GPS RegisterNUICallback('setGPS', function(data, cb) SetNewWaypoint(tonumber(data.x), tonumber(data.y)) cb() end) -- Prendre une photo avec l'appareil photo du téléphone RegisterNUICallback('takePhoto', function(data, cb) CreateMobilePhone(1) CellCamActivate(true, true) -- Gestion de la capture photo... end) ``` ## API JavaScript - PhoneAPI Classe JavaScript centrale pour la communication entre l'interface Vue.js et le backend FiveM. ```javascript // Instance PhoneAPI - Gestion des messages async sendMessage(phoneNumber, message) { return this.post('sendMessage', { phoneNumber, message }) } async deleteMessage(id) { return this.post('deleteMessage', { id }) } async setMessageRead(number) { return this.post('setReadMessageNumber', { number }) } // Gestion des contacts async addContact(display, phoneNumber) { return this.post('addContact', { display, phoneNumber }) } async updateContact(id, display, phoneNumber) { return this.post('updateContact', { id, display, phoneNumber }) } async deleteContact(id) { return this.post('deleteContact', { id }) } // Gestion des appels avec WebRTC async startCall(numero, extraData = undefined) { if (USE_VOICE_RTC === true) { const rtcOffer = await this.voiceRTC.prepareCall() return this.post('startCall', { numero, rtcOffer, extraData }) } else { return this.post('startCall', { numero, extraData }) } } async acceptCall(infoCall) { if (USE_VOICE_RTC === true) { const rtcAnswer = await this.voiceRTC.acceptCall(infoCall) return this.post('acceptCall', { infoCall, rtcAnswer }) } else { return this.post('acceptCall', { infoCall }) } } ``` ## API JavaScript - Twitter Fonctionnalités de l'application Twitter intégrée au téléphone. ```javascript // Connexion au compte Twitter twitter_login(username, password) { this.post('twitter_login', { username, password }) } // Créer un nouveau compte Twitter twitter_createAccount(username, password, avatarUrl) { this.post('twitter_createAccount', { username, password, avatarUrl }) } // Publier un tweet twitter_postTweet(username, password, message) { this.post('twitter_postTweet', { username, password, message }) } // Aimer/Retirer le like d'un tweet twitter_toggleLikeTweet(username, password, tweetId) { this.post('twitter_toggleLikeTweet', { username, password, tweetId }) } // Récupérer les tweets twitter_getTweets(username, password) { this.post('twitter_getTweets', { username, password }) } // Changer l'avatar du profil twitter_setAvatar(username, password, avatarUrl) { this.post('twitter_setAvatarUrl', { username, password, avatarUrl }) } ``` ## Configuration des Postes Fixes Configuration Lua pour définir des téléphones fixes à des emplacements spécifiques sur la carte. ```lua -- Configuration dans config.lua FixePhone = { -- Poste de police - numéro 911 ['911'] = { name = "Central Police", coords = { x = 441.2, y = -979.7, z = 30.58 } }, -- Cabine téléphonique publique ['008-0001'] = { name = "Cabine Telephonique", coords = { x = 372.25, y = -965.75, z = 28.58 } }, -- Exemple: Ajouter un poste à l'hôpital ['112'] = { name = "Standard Hôpital", coords = { x = 298.5, y = -584.2, z = 43.26 } } } -- Option pour afficher le numéro dans les notifications ShowNumberNotification = false ``` ## Configuration JSON - Interface Téléphone Configuration complète de l'apparence et des fonctionnalités du téléphone. ```json { "reseau": "MonServeur", "themeColor": "#303f9f", "useFormatNumberFrance": false, "useWebRTCVocal": true, "RTCConfig": { "iceServers": [{ "urls": ["turn:votre-serveur.com"], "username": "utilisateur", "credential": "motdepasse" }] }, "background": { "Default": "back001.jpg", "City Night": "back002.jpg", "Custom URL": "URL" }, "coque": { "Samsung S8": "s8.png", "iPhone X": "iphonex.png", "Transparent": "transparent.png" }, "serviceCall": [ { "display": "Police", "backgroundColor": "blue", "subMenu": [ { "title": "Appeler le 911", "eventName": "gcphone:autoCallNumber", "type": { "number": "911" } } ] } ], "defaultContacts": [ { "number": "911", "display": "Police", "backgroundColor": "blue" }, { "number": "ambulance", "display": "Ambulance", "backgroundColor": "red" } ], "apps": [ { "name": "Téléphone", "icons": "/html/static/img/icons_app/call.png", "routeName": "appels", "enabled": true }, { "name": "Messages", "icons": "/html/static/img/icons_app/sms.png", "routeName": "messages", "puceRef": "nbMessagesUnread" }, { "name": "Contacts", "icons": "/html/static/img/icons_app/contacts.png", "routeName": "contacts" }, { "name": "Twitter", "icons": "/html/static/img/icons_app/twitter.png", "routeName": "twitter" }, { "name": "Bank", "icons": "/html/static/img/icons_app/bank.png", "routeName": "bank" } ] } ``` ## Schéma Base de Données Structure SQL pour la persistance des données du téléphone. ```sql -- Ajouter le numéro de téléphone à la table users ALTER TABLE `users` ADD COLUMN `phone_number` VARCHAR(10) NULL; -- Table des contacts CREATE TABLE IF NOT EXISTS `phone_users_contacts` ( `id` int(11) NOT NULL AUTO_INCREMENT, `identifier` varchar(60) DEFAULT NULL, `number` varchar(10) DEFAULT NULL, `display` varchar(64) NOT NULL DEFAULT '-1', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- Table des messages SMS CREATE TABLE IF NOT EXISTS `phone_messages` ( `id` int(11) NOT NULL AUTO_INCREMENT, `transmitter` varchar(10) NOT NULL, `receiver` varchar(10) NOT NULL, `message` varchar(255) NOT NULL DEFAULT '0', `time` timestamp NOT NULL DEFAULT current_timestamp(), `isRead` int(11) NOT NULL DEFAULT 0, `owner` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- Table de l'historique des appels CREATE TABLE IF NOT EXISTS `phone_calls` ( `id` int(11) NOT NULL AUTO_INCREMENT, `owner` varchar(10) NOT NULL, `num` varchar(10) NOT NULL, `incoming` int(11) NOT NULL, `time` timestamp NOT NULL DEFAULT current_timestamp(), `accepts` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- Table du chat anonyme CREATE TABLE IF NOT EXISTS `phone_app_chat` ( `id` int(11) NOT NULL AUTO_INCREMENT, `channel` varchar(20) NOT NULL, `message` varchar(255) NOT NULL, `time` timestamp NOT NULL DEFAULT current_timestamp(), PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` ## Intégration ESX - Services d'Urgence Module esx_addons_gcphone pour l'intégration avec les métiers ESX et les services d'urgence. ```lua -- Enregistrer un numéro de service (dans le script du métier) AddEventHandler('esx_phone:registerNumber', function(number, type, sharePos, hasDispatch, hideNumber, hidePosIfAnon) print('Enregistrement du telephone ' .. number .. ' => ' .. type) PhoneNumbers[number] = { type = type, sources = {}, alerts = {} } end) -- Exemple d'utilisation dans un script de métier police TriggerEvent('esx_phone:registerNumber', 'police', 'police', true, true, false, false) -- Envoyer un message aux services (depuis un script client) TriggerServerEvent('esx_addons_gcphone:startCall', 'police', 'Besoin d\'assistance', { x = playerCoords.x, y = playerCoords.y }) -- Gestion automatique des changements de métier AddEventHandler('esx:setJob', function(source, job, lastJob) if PhoneNumbers[lastJob.name] ~= nil then TriggerEvent('esx_addons_gcphone:removeSource', lastJob.name, source) end if PhoneNumbers[job.name] ~= nil then TriggerEvent('esx_addons_gcphone:addSource', job.name, source) end end) ``` ## Application Banque - Intégration ESX Synchronisation du solde bancaire ESX avec l'application banque du téléphone. ```lua -- Mise à jour du solde bancaire local bank = 0 function setBankBalance(value) bank = value SendNUIMessage({ event = 'updateBankbalance', banking = bank }) end -- Récupération du solde à la connexion du joueur RegisterNetEvent('esx:playerLoaded') AddEventHandler('esx:playerLoaded', function(playerData) local accounts = playerData.accounts or {} for index, account in ipairs(accounts) do if account.name == 'bank' then setBankBalance(account.money) break end end end) -- Mise à jour en temps réel lors des transactions RegisterNetEvent('esx:setAccountMoney') AddEventHandler('esx:setAccountMoney', function(account) if account.name == 'bank' then setBankBalance(account.money) end end) ``` ## Application Chat Anonyme Système de chat anonyme par canaux pour les communications secrètes entre joueurs. ```lua -- Côté client - Envoyer un message dans un canal RegisterNUICallback('tchat_addMessage', function(data, cb) TriggerServerEvent('gcPhone:tchat_addMessage', data.channel, data.message) end) -- Côté client - Récupérer les messages d'un canal RegisterNUICallback('tchat_getChannel', function(data, cb) TriggerServerEvent('gcPhone:tchat_channel', data.channel) end) -- Côté client - Recevoir un nouveau message RegisterNetEvent("gcPhone:tchat_receive") AddEventHandler("gcPhone:tchat_receive", function(message) SendNUIMessage({ event = 'tchat_receive', message = message }) end) -- Côté client - Recevoir les messages du canal RegisterNetEvent("gcPhone:tchat_channel") AddEventHandler("gcPhone:tchat_channel", function(channel, messages) SendNUIMessage({ event = 'tchat_channel', messages = messages }) end) ``` ## Résumé GCPhone est conçu pour les administrateurs de serveurs FiveM qui souhaitent offrir une expérience de jeu de rôle immersive avec un système de communication complet. Le projet s'intègre nativement avec le framework ESX et supporte les appels WebRTC pour une qualité vocale optimale. Les principales utilisations incluent la communication entre joueurs via SMS et appels, l'intégration avec les métiers (police, ambulance, etc.), et les applications sociales comme Twitter. L'architecture modulaire permet une personnalisation extensive via les fichiers de configuration JSON et Lua. Les développeurs peuvent ajouter de nouvelles applications, modifier l'apparence du téléphone, configurer des postes fixes à des emplacements stratégiques, et intégrer le système avec d'autres scripts ESX. La persistance MySQL assure la sauvegarde des contacts, messages et historique d'appels entre les sessions de jeu.