> For the complete documentation index, see [llms.txt](https://safky.gitbook.io/product-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://safky.gitbook.io/product-docs/scripts/s_phone.md).

# PHONE

Smartphone with two devices, calls, messages, camera, social apps and the city's services, for ESX, QBCore, QBX and standalone.

A smartphone with two devices (a slab and a foldable), calls with voice, messages, camera and gallery, social apps and the city's services, in one resource.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-ca59a794ad508775b1f57699ad87d4b11e07ff4d%2F01-two-phones.jpg?alt=media" alt="Three phones: the Phone on the home screen, the Phone Double closed, and the Double unfolded with Messages and Maps side by side."><figcaption><p>The Phone, the Phone Double closed, and the Double unfolded with two apps side by side.</p></figcaption></figure>

***

## ✅ Requirements

|                      |                                                                                                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Frameworks**       | ESX, QBCore, QBX (detected on start), or standalone                                                                                                                |
| **Dependencies**     | [ox\_lib](https://github.com/overextended/ox_lib), [oxmysql](https://github.com/overextended/oxmysql), and `s_phone_props`, the second resource of the package     |
| **Voice**            | [pma-voice](https://github.com/AvarianKnight/pma-voice) or mumble-voip. Without one, calls connect with no audio                                                   |
| **Uploads**          | A [Fivemanage](https://fivemanage.com) API key, or your own endpoint through `Hooks.UploadMedia`                                                                   |
| **Inventories**      | For `Identity = 'item'`: ox\_inventory, qb-inventory, ps-inventory, lj-inventory or qs-inventory. Any other inventory runs as `'character'`                        |
| **Company accounts** | Optional: Renewed-Banking, qb-banking, okokBanking, qb-management or esx\_addonaccount, for the Services vault and the company share of invoices and EatHub orders |

***

## 📥 Installation

The package has two resources, `s_phone` and `s_phone_props`, each its own download. Both go in your resources.

```
ensure ox_lib
ensure oxmysql
ensure pma-voice
ensure s_phone_props
ensure s_phone
```

There is no SQL to import. The tables are created on the first start.

Add `charset=utf8mb4` to your `mysql_connection_string` if it is not there. Without it, messages with emoji fail to save, and the console says so on start.

### Items

Two items, `phone` and `phone_double`, one per model. The blocks for ox\_inventory, QBCore, QBX and ESX are in `install/items.md`, the images in `install/images/`. The names must match `item` in `Config.Models`.

{% hint style="warning" %}
ox\_inventory, QBCore and QBX already ship a `phone` item. Replace that entry with the one from `install/items.md`. In a Lua table the last key wins, so a block pasted above the original is silently overwritten by it.
{% endhint %}

In ox\_inventory each item carries `client = { export = 's_phone.UseItem' }`, already in the block. That is what opens the phone on use, with or without a framework.

Standalone has no items. The phone opens with the key, and the server runs with `Item.Require = false` on its own.

### Keys and webhooks

Both live in `config/sv-functions.lua`, outside the escrow, and never reach a player.

| Key                          | Needed for                                                                               |
| ---------------------------- | ---------------------------------------------------------------------------------------- |
| `Keys.Fivemanage`            | Photos, videos and voice notes with `Config.Media.Provider = 'fivemanage'` (the default) |
| `Keys.Tenor` or `Keys.Giphy` | The GIF button in Messages, with `Config.Messages.Gifs = 'tenor'` or `'giphy'`           |

The console says on start when the provider you picked has no key. `Webhooks`, in the same file, takes one Discord URL per category; an empty string turns that category off.

### External hosts

The phone talks to these hosts. Block one and that part stops.

| Host                                    | What                   | Option                                         |
| --------------------------------------- | ---------------------- | ---------------------------------------------- |
| `safky-maptiles.pages.dev`              | Map tiles              | `Config.Maps.Tiles.Url`, or host your own copy |
| `api.fivemanage.com`                    | Uploads                | `Config.Media.Provider`                        |
| `tenor.googleapis.com`, `api.giphy.com` | GIF search             | `Config.Messages.Gifs`                         |
| `api.deezer.com`                        | Loopz music search     | `Config.Loopz.Music`                           |
| `stun.l.google.com`                     | Video calls and lives  | `Config.Video.IceServers`                      |
| `raw.githubusercontent.com`             | Version check on start |                                                |

Images pasted by link are only loaded from the hosts in `Config.Media.LinkHosts` (imgur and Discord by default). See [Media, camera and video](/product-docs/scripts/s_phone/media.md).

***

## ⚙️ Configuration

Everything in `config/` is open. Every option in `config/config.lua` is commented in the file; this page covers the ones that need more than a comment, and the pages below cover each group of apps.

### Identity

```lua
Identity = 'character',
```

* `'character'`: one number per character. The item only unlocks the phone and picks the model. Works with every inventory.
* `'item'`: every phone item carries its own number in its metadata. Two phones in the inventory are two numbers, and using the other one switches. Needs an inventory with item metadata (see Requirements). With any other inventory the server runs as `'character'` and says so in the console.

With `'item'` the phone items must not stack: the blocks in `install/items.md` already set `stack = false` and `unique = true`.

### Models

`Config.Models` has one entry per phone item: `phone` (a slab) and `double` (a foldable that opens into a wide screen). `item` is the inventory item that opens it, `prop.model` the model in the hand. Both props come from `s_phone_props`.

`DefaultModel` is the model used when no item decides it (`Item.Require = false`), and the first choice when a player carries more than one phone item.

### Numbers

```lua
Number = {
    Prefixes = { '555' },
    Length = 7,
    Format = '###-####',
    ImportExisting = true,
},
```

A new number starts with one of `Prefixes` and has `Length` digits in total. With `ImportExisting = true` a character's first phone keeps the number the framework already stores (`charinfo.phone` on QBCore and QBX, `users.phone_number` on ESX), when it has `Length` digits. The number assigned is written back there, so scripts that read the framework's field stay right. Both steps are hooks in `config/sv-functions.lua`: `Hooks.ImportExistingNumber` and `Hooks.OnNumberAssigned`.

### Opening the phone

`Keys.Open` is the one key: it opens and closes the phone (`F1` by default). It applies on a player's first join; after that they change it in the game's Key Bindings. `OpenCommand` is the command (`/phone`).

With `KeepInput = true` the player keeps moving with the phone open, and the game controls pause only while a text field has focus. `false` makes the phone take every key, like a normal menu.

### Currency and languages

```lua
Currency = { Symbol = '€', After = true, Thousands = ' ' },   -- reads 1 000 €
```

`Locale` is the server's language, a file name in `config/locales/`. `Languages` lists the ones a player can pick for their own phone in Settings. A new translation is a file in `config/locales/` plus a line in `Languages`; keys missing from it fall back to English.

### Apps

```lua
Apps = {
    Store = { 'crush', 'darkchat', 'news', 'loopz', 'onlyvibes', 'yapp', 'instavibe', 'marketplace',
        'lane', 'ricochet', 'cowfly', 'pairs', 'stacks', 'mogul', 'four', 'fleet', 'checkers', 'carpetking', 'towline', 'bagup', 'rydehub', 'music' },
    Featured = { 'instavibe', 'marketplace', 'crush', 'eathub' },
    FeaturedGames = { 'mogul', 'cowfly' },
    Hidden = {},
    Jobs = {
        darkchat = { 'police' },
    },
},
```

* Apps in `Store` come from the App Store: players install and remove them. Every other city app comes installed and can be removed. The apps that come with the phone are always on it.
* `Featured` fills the big cards of the Store's Today page, in order; `FeaturedGames` fills the Games section under them. A custom app from another resource can go in either list by its id.
* `Hidden` takes an app off every phone and out of the Store.
* `Jobs` keeps an app to some jobs, `{ 'police', 'ambulance' }`, or from a grade up, `{ police = 2 }`. Nobody else sees it and the server refuses it to them. Needs ESX, QBCore or QBX.

Phone, Messages, Camera, Photos, Clock, Wallet, Settings and the App Store cannot be hidden or kept to a job.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-bafdcc4b11aab8ca1427de560061120030040300%2F02-apps.jpg?alt=media" alt="The Today page of the App Store on the Phone, in front of the home screen of the unfolded Double."><figcaption><p>The Today page of the App Store: the featured cards come from <code>Apps.Featured</code>, the Games section from <code>Apps.FeaturedGames</code>.</p></figcaption></figure>

### Look

Wallpapers are `.webp` files in `web/wallpapers/`, ringtones and text tones `.wav` files in `web/sounds/`. Add your own files and list them by name in `Wallpapers`, `Ringtones` and `TextTones`. `WallpaperCategories` groups the wallpapers under headings in Settings; leave it out for one grid.

{% hint style="info" %}
An update replaces `web/`. Keep a copy of the files you added to `web/wallpapers/`, `web/sounds/` and `web/music/`, and put them back after updating.
{% endhint %}

### The other pages

| Page                                                                    | What it covers                                                        |
| ----------------------------------------------------------------------- | --------------------------------------------------------------------- |
| [Core apps](/product-docs/scripts/s_phone/apps.md)                      | Calls, Messages, Wallet, Maps, Files, Passwords, Mail, Clock, Weather |
| [Media, camera and video](/product-docs/scripts/s_phone/media.md)       | Uploads, image links, the camera, video calls and lives, voice        |
| [Social](/product-docs/scripts/s_phone/social.md)                       | InstaVibe, Yapp, Loopz, Crush, DarkChat, OnlyVibes, News, Marketplace |
| [City and economy](/product-docs/scripts/s_phone/city.md)               | Invoices, Services, EatHub, RydeHub, Casino, Games, Music             |
| [Garages and housing](/product-docs/scripts/s_phone/garages-housing.md) | The garage and housing scripts the two apps read, and the valet       |
| [Custom apps](/product-docs/scripts/s_phone/custom-apps.md)             | An app of your own inside the phone, and server actions for it        |
| [Integration](/product-docs/scripts/s_phone/integration.md)             | Exports, events, hooks, webhooks and the database                     |

***

## 💬 Commands

| Command                                          | Who           | What it does                                 |
| ------------------------------------------------ | ------------- | -------------------------------------------- |
| `/phone`                                         | Everyone      | Opens and closes the phone (same as the key) |
| `/phone-resetcode <player id or number>`         | `group.admin` | Clears a forgotten passcode and face unlock  |
| `/yapp-verify <handle> <blue\|gold\|grey\|none>` | `group.admin` | Sets or clears a Yapp badge                  |
| `/loopz-verify <username> [on\|off]`             | `group.admin` | Gives or takes the Loopz badge               |

`/phone` is `OpenCommand`. The other three are `ResetCodeCommand`, `Yapp.VerifyCommand` and `Loopz.VerifyCommand` in `config/config.lua`: each takes a `name` and the ACE in `restricted`, and `false` turns the command off.
