# AppsMobs : pack de connaissances pour assistant IA

Tu es un assistant qui aide l'utilisateur à utiliser **AppsMobs** et à écrire des scripts pour l'appli.
Réponds en français, de façon concrète, et donne des scripts prêts à coller dans l'éditeur.

## C'est quoi AppsMobs
Application Windows qui automatise un téléphone ou un émulateur Android (LDPlayer, etc.) : toucher, glisser,
reconnaître une image ou un texte à l'écran, lancer des applis. Les scripts sont en **Python**, ou en
**blocs** dans l'éditeur sans code (l'éditeur en blocs).

## Utiliser l'appli
1. Brancher un téléphone (débogage USB activé) ou démarrer un émulateur : il apparaît dans le Tableau de bord.
2. Ouvrir le **Playground**, l'éditeur de scripts intégré. Le panneau « Fonctions » liste les fonctions avec
   recherche, et l'autocomplétion affiche la signature pendant la frappe.
3. Écrire le script, puis appuyer sur **Run**. Le résultat détaillé est dans la Console de l'appli.
4. Les images à reconnaître (.png) se mettent **dans le dossier du script**. On les obtient par capture
   d'écran de l'appareil, recadrée sur le bouton ou l'icône voulu.
5. Les coordonnées (x, y) sont celles de l'image de l'appareil affichée dans AppsMobs.
6. Doc complète : https://appsmobs.com/fr/docs

## Comment répondre
- Un script = du code Python brut, sans `main()`, sans import, sans appel final.
- Prévoir un `timeout` et tester le résultat des attentes (`None` / `False` si rien n'est trouvé).
- Si l'utilisateur doit fournir une image, dire quel nom de fichier utiliser et ce qu'elle doit montrer.
- Ne jamais inventer de fonction : la liste ci-dessous est la seule référence.

## Exemple complet
```python
# Toucher un bouton à chaque apparition, 10 fois
for i in range(10):
    log(f"Tour {i + 1}/10")
    if not tap_image("bouton.png", timeout=20):
        log("Bouton introuvable, arrêt")
        break
    wait(2)

log("Terminé !")
```

## Règles d'un script AppsMobs (Python)
- Le code est exécuté tel quel, de haut en bas : pas de fonction main(), pas de my_script(), aucun appel à la fin.
- Aucun import pour les fonctions AppsMobs : elles sont déjà disponibles. Ne jamais écrire "from core...".
- Les fonctions reçoivent l'appareil toutes seules : tap(540, 1200), jamais tap(android_client, ...).
- wait() compte en SECONDES (wait(2) = deux secondes). Les seuls paramètres en millisecondes s'appellent duration_ms.
- Les images (.png) sont cherchées dans le dossier du script, puis dans Documents/AppsMobs/Scripts/img. Une image absente provoque une erreur claire.
- conf : ressemblance minimale de 0 à 1 (0.8 par défaut). region : zone de recherche (x, y, largeur, hauteur), pas deux coins.
- Les attentes ont un timeout (30 s par défaut) et rendent None ou False s'il est dépassé : teste le résultat.
- Les fonctions de texte (tap_text, wait_text, read_text_at…) lisent l'interface Android : elles ne voient rien dans un jeu. Dans un jeu, utiliser une image ou l'OCR (ocr_text, en fin de liste).
- log() prend un seul texte (utiliser une f-string : log(f"score {s}")).
- Pour arrêter le script avant la fin : finish().
- N'invente aucune fonction : utilise uniquement la liste ci-dessous et Python standard.

## 👆 Gestes

### `tap(x, y)`

**Toucher.** Toucher l'écran au point (x, y). Les coordonnées sont celles de l'image de l'appareil affichée dans AppsMobs.

```python
tap(540, 1200)
```

### `double_tap(x, y)`

**Double toucher.** Deux touchers rapides au même point.

```python
double_tap(540, 1200)
```

### `long_press(x, y, duration_ms=1000)`

**Appui long.** Garder le doigt posé duration_ms millisecondes.

```python
long_press(540, 1200, duration_ms=1500)
```

### `swipe(x1, y1, x2, y2, duration_ms=500)`

**Glisser.** Glisser le doigt d'un point à un autre, comme un humain (le geste ralentit à la fin).

```python
swipe(540, 1600, 540, 600)
```

### `swipe_up(distance=None, duration_ms=500)`

**Glisser vers le haut.** Le doigt monte : la page défile vers le bas. distance en pixels (40 % de l'écran par défaut).

```python
swipe_up()
```

### `swipe_down(distance=None, duration_ms=500)`

**Glisser vers le bas.** Le doigt descend : la page remonte.

```python
swipe_down()
```

### `swipe_left(distance=None, duration_ms=500)`

**Glisser vers la gauche.** Le doigt va vers la gauche (page suivante d'un carrousel).

```python
swipe_left()
```

### `swipe_right(distance=None, duration_ms=500)`

**Glisser vers la droite.** Le doigt va vers la droite (page précédente).

```python
swipe_right()
```

### `drag(x1, y1, x2, y2, duration_ms=1000)`

**Déplacer.** Appui long sur le premier point puis, sans relever le doigt, glisser jusqu'au second : déplacer une icône, un objet.

```python
drag(200, 800, 800, 800)
```

### `pinch_in(x=None, y=None)`

**Pincer (dézoomer).** Rapprocher deux doigts vers le point (centre de l'écran par défaut). Flux scrcpy requis : sans lui, aucun zoom.

```python
pinch_in()
```

### `pinch_out(x=None, y=None)`

**Écarter (zoomer).** Écarter deux doigts depuis le point. Flux scrcpy requis.

```python
pinch_out()
```

### `touch_down(x, y)`

**Poser le doigt.** Poser le doigt sans le relever (avec touch_up pour un geste sur mesure). scrcpy requis.

```python
touch_down(540, 1200)
```

### `touch_up(x, y)`

**Relever le doigt.** Relever le doigt posé par touch_down.

```python
touch_up(540, 1200)
```

## 🎯 Images à l'écran

### `find_image(img, conf=0.8, region=None, timeout=0)`

**Trouver une image.** Position (x, y) du centre de l'image à l'écran, ou None. timeout : secondes d'attente (0 = un seul essai).

```python
pos = find_image("bouton.png")
if pos:
    tap(*pos)
```

### `image_visible(img, conf=0.8, region=None)`

**Image visible ?.** True si l'image est à l'écran maintenant, False sinon.

```python
if image_visible("popup.png"):
    press_back()
```

### `wait_image(img, conf=0.8, region=None, timeout=30)`

**Attendre une image.** Attendre que l'image apparaisse. Rend (x, y), ou None après timeout secondes : le script continue. timeout=None : attendre sans fin.

```python
if wait_image("accueil.png", timeout=20):
    log("chargé")
```

### `find_image_positions(img, conf=0.8, region=None)`

**Toutes les positions.** Liste de toutes les positions (x, y) où l'image apparaît, de haut en bas.

```python
for x, y in find_image_positions("piece.png"):
    tap(x, y)
```

### `first_image(imgs, conf=0.8, region=None, timeout=30)`

**Première image de la liste.** Attendre que l'une des images apparaisse. Rend (image, x, y), ou None après timeout.

```python
res = first_image(["gagne.png", "perdu.png"])
if res:
    nom, x, y = res
```

### `tap_image(img, conf=0.8, region=None, timeout=30, offset=(0, 0))`

**Toucher une image.** Attendre l'image (30 s par défaut) puis la toucher. offset=(dx, dy) décale le toucher. Rend True si touchée.

```python
tap_image("jouer.png")
```

### `double_tap_image(img, conf=0.8, region=None, timeout=30, offset=(0, 0))`

**Double toucher une image.** Attendre l'image puis la toucher deux fois.

```python
double_tap_image("photo.png")
```

### `long_press_image(img, duration_ms=1000, conf=0.8, region=None, timeout=30)`

**Appui long sur une image.** Attendre l'image puis garder le doigt dessus.

```python
long_press_image("icone.png", duration_ms=1500)
```

### `tap_first_image(imgs, conf=0.8, region=None, timeout=30, offset=(0, 0))`

**Toucher la première image trouvée.** Toucher la première image de la liste qui apparaît. Rend son nom, ou None.

```python
choix = tap_first_image(["ok.png", "fermer.png"])
```

### `tap_all_images(img, conf=0.8, region=None)`

**Toucher toutes les occurrences.** Toucher chaque endroit où l'image apparaît. Rend le nombre de touchers.

```python
n = tap_all_images("coeur.png")
```

### `scroll_to_image(img, direction='down', max_swipes=20, conf=0.8, region=None)`

**Défiler jusqu'à une image.** Faire défiler la page (direction "down" ou "up") jusqu'à voir l'image. Rend (x, y) ou None.

```python
pos = scroll_to_image("article.png")
```

### `tap_until_image(positions, img, max_taps=10, conf=0.8, region=None)`

**Toucher jusqu'à voir une image.** Toucher les positions à tour de rôle jusqu'à ce que l'image apparaisse. Rend True si elle est apparue.

```python
tap_until_image([(540, 1800)], "suite.png")
```

## 🔤 Texte à l'écran

### `tap_text(text, exact=True, timeout=30)`

**Toucher un texte.** Attendre (30 s par défaut) l'élément qui affiche ce texte puis le toucher. Rend True si touché. exact=False : contient le texte.

```python
tap_text("Paramètres")
```

### `text_visible(text, exact=True)`

**Texte visible ?.** True si un élément affiche ce texte maintenant.

```python
if text_visible("Connexion"):
    log("écran de connexion")
```

### `wait_text(text, exact=True, timeout=30)`

**Attendre un texte.** Attendre qu'un élément affiche ce texte. Rend (x, y), ou None après timeout.

```python
wait_text("Bienvenue", timeout=20)
```

### `read_text_at(x, y)`

**Lire le texte à un point.** Texte de l'élément sous le point (x, y), ou None.

```python
prix = read_text_at(540, 900)
```

### `read_text_by_id(resource_id)`

**Lire le texte d'un élément.** Texte de l'élément d'identifiant Android donné ("com.app:id/titre" ou juste "titre"), ou None.

```python
titre = read_text_by_id("com.app:id/titre")
```

## ⌨️ Saisie & touches

### `type_text(text)`

**Taper du texte.** Taper du texte dans le champ actif. Les accents et emojis ne passent qu'avec le flux scrcpy.

```python
type_text("bonjour")
```

### `clear_text()`

**Effacer le champ.** Effacer le contenu du champ actif (jusqu'à max_chars caractères avant Android 13).

```python
clear_text()
```

### `select_all()`

**Tout sélectionner.** Sélectionner tout le texte du champ actif. Android 13 ou plus. Rend True si fait.

```python
select_all()
```

### `copy()`

**Copier.** Copier la sélection.

```python
copy()
```

### `paste()`

**Coller.** Coller dans le champ actif.

```python
paste()
```

### `set_clipboard(text)`

**Remplir le presse-papiers.** Mettre du texte dans le presse-papiers de l'appareil. Flux scrcpy requis. Rend True si fait.

```python
set_clipboard("code promo")
```

### `get_clipboard()`

**Lire le presse-papiers.** Texte du presse-papiers, ou None. Flux scrcpy requis.

```python
texte = get_clipboard()
```

### `press_back(times=1)`

**Touche Retour.** Appuyer sur Retour (times fois).

```python
press_back()
```

### `press_home()`

**Touche Accueil.** Revenir à l'écran d'accueil.

```python
press_home()
```

### `press_enter()`

**Touche Entrée.** Valider le champ actif.

```python
press_enter()
```

### `recent_apps()`

**Applications récentes.** Ouvrir la liste des applications récentes.

```python
recent_apps()
```

## 📱 Écran

### `screen_size()`

**Taille de l'écran.** (largeur, hauteur), dans les coordonnées de tap().

```python
w, h = screen_size()
```

### `pixel_color(x, y)`

**Couleur d'un pixel.** Couleur (bleu, vert, rouge) du pixel, ou None si l'écran n'est pas disponible.

```python
couleur = pixel_color(10, 10)
if couleur:
    b, g, r = couleur
```

### `pixel_is(x, y, color, tolerance=10)`

**Pixel de cette couleur ?.** True si le pixel a la couleur (bleu, vert, rouge), à tolerance près.

```python
if pixel_is(100, 200, (0, 0, 255)):
    log("rouge")
```

### `screenshot(filename='screenshot.png')`

**Capture d'écran.** Enregistrer une capture. Rend le chemin du fichier.

```python
screenshot("capture.png")
```

### `scroll_to_top(max_swipes=3)`

**Remonter en haut.** Remonter en haut de la page.

```python
scroll_to_top()
```

### `scroll_to_bottom(max_swipes=3)`

**Descendre en bas.** Descendre en bas de la page.

```python
scroll_to_bottom()
```

### `pull_to_refresh()`

**Tirer pour actualiser.** Tirer le haut de la page vers le bas pour l'actualiser.

```python
pull_to_refresh()
```

## 📦 Applications

### `open_app(package, activity=None)`

**Ouvrir une application.** Lancer une application par son nom de paquet (visible dans installed_apps()).

```python
open_app("com.android.chrome")
```

### `close_app(package=None)`

**Fermer une application.** Arrêter une application (sans paquet : celle au premier plan). Rend True si arrêtée.

```python
close_app("com.android.chrome")
```

### `restart_app(package)`

**Relancer une application.** Arrêter puis relancer une application.

```python
restart_app("com.android.chrome")
```

### `clear_app_data(package)`

**Effacer les données d'une app.** Efface TOUTES les données de l'application (connexion, réglages), comme une réinstallation.

```python
clear_app_data("com.exemple.jeu")
```

### `install_app(apk)`

**Installer une application.** Installer un fichier .apk depuis le PC. Rend True si installée.

```python
install_app("C:/apk/app.apk")
```

### `uninstall_app(package)`

**Désinstaller une application.** Désinstaller une application. Rend True si désinstallée.

```python
uninstall_app("com.exemple.app")
```

### `installed_apps(system_apps=False)`

**Applications installées.** Liste des paquets installés (system_apps=True : applications système comprises).

```python
for p in installed_apps():
    log(p)
```

### `app_info(package)`

**Informations de l'application.** Ouvrir la page Informations de l'application dans les réglages.

```python
app_info("com.android.chrome")
```

## ⚙️ Appareil & système

### `wake_screen()`

**Allumer l'écran.** Sortir l'écran de veille (sans effet s'il est allumé). Ne déverrouille pas.

```python
wake_screen()
```

### `reboot()`

**Redémarrer l'appareil.** Redémarrer l'appareil.

```python
reboot()
```

### `device_info()`

**Infos de l'appareil.** Dictionnaire : "model" (modèle), "android_version", "serial" (numéro de série).

```python
info = device_info()
log(info["model"])
```

### `adb(command)`

**Commande adb.** Lancer une commande adb shell et rendre sa sortie.

```python
modele = adb("getprop ro.product.model")
```

### `set_wifi(on=True)`

**Wi-Fi.** Activer (True) ou couper (False) le Wi-Fi.

```python
set_wifi(False)
```

### `set_mobile_data(on=True)`

**Données mobiles.** Activer ou couper les données mobiles.

```python
set_mobile_data(True)
```

### `set_airplane_mode(on=True)`

**Mode avion.** Activer ou couper le mode avion (Android 11 ou plus).

```python
set_airplane_mode(True)
```

### `rotate(orientation='landscape')`

**Orientation.** Bloquer l'orientation : "portrait", "landscape", "reverse_portrait" ou "reverse_landscape".

```python
rotate("landscape")
```

### `volume_up()`

**Monter le volume.** Monter le volume.

```python
volume_up()
```

### `volume_down()`

**Baisser le volume.** Baisser le volume.

```python
volume_down()
```

### `mute()`

**Couper le son.** Couper ou remettre le son (certains téléphones ignorent cette touche).

```python
mute()
```

### `open_notifications()`

**Ouvrir les notifications.** Ouvrir le volet des notifications.

```python
open_notifications()
```

### `close_notifications()`

**Fermer les notifications.** Fermer le volet des notifications.

```python
close_notifications()
```

### `open_settings(category=None)`

**Ouvrir les réglages.** Ouvrir les réglages Android, ou une catégorie : "wifi", "bluetooth", "display", "battery", "apps", "security", "location", "sound", "notification", "storage". Rend True si ouverts.

```python
open_settings("wifi")
```

### `vibrate(duration_ms=1000)`

**Vibrer.** Faire vibrer l'appareil duration_ms millisecondes. Rend True si Android a accepté.

```python
vibrate(500)
```

## 📞 Téléphone

### `send_sms(number, message)`

**Envoyer un SMS.** Ouvrir l'appli SMS avec le message écrit puis toucher Envoyer. Rend True si envoyé, False si le bouton Envoyer est introuvable (le SMS reste prêt à l'écran).

```python
if not send_sms("+33600000000", "Bonjour"):
    log("SMS non envoyé")
```

### `read_sms(limit=10)`

**Lire les SMS.** Derniers SMS reçus, du plus récent : liste de dictionnaires "number", "message", "date" (millisecondes). Vide si Android refuse la lecture.

```python
for sms in read_sms(5):
    log(f"{sms['number']} : {sms['message']}")
```

### `call(number)`

**Appeler.** Appeler un numéro. Rend True si l'appel est lancé.

```python
call("+33600000000")
```

### `answer_call()`

**Décrocher.** Décrocher l'appel entrant. Rend False si aucun appel ne sonne.

```python
answer_call()
```

### `hang_up()`

**Raccrocher.** Raccrocher.

```python
hang_up()
```

### `contacts(limit=50)`

**Contacts.** Contacts avec un numéro : liste de dictionnaires "name", "number". Vide si Android refuse la lecture.

```python
for c in contacts():
    log(f"{c['name']} : {c['number']}")
```

### `set_alarm(hour, minute, label='Alarme')`

**Créer une alarme.** Créer une alarme dans l'appli Horloge (heure 0-23). Rend True si acceptée.

```python
set_alarm(7, 30, "Réveil")
```

### `cancel_alarm(hour=None, minute=None)`

**Désactiver une alarme.** Désactiver l'alarme de cette heure (sans heure : toutes). L'horloge peut demander une confirmation.

```python
cancel_alarm(7, 30)
```

### `alarms()`

**Alarmes.** Liste des alarmes ("hour", "minute", "enabled", "label"). Souvent vide : la plupart des horloges ne partagent pas leurs alarmes.

```python
log(alarms())
```

### `set_timer(hours=0, minutes=0, seconds=0)`

**Minuteur.** Lancer un minuteur dans l'appli Horloge (1 s à 24 h). Rend True si accepté.

```python
set_timer(minutes=5)
```

## ⏱️ Temps & déroulé

### `wait(seconds)`

**Attendre.** Attendre en SECONDES (0.5 = une demi-seconde). Le bouton Pause du Playground est respecté.

```python
wait(2)
```

### `random_wait(min_s=0.35, max_s=0.8)`

**Attendre au hasard.** Attendre une durée au hasard entre min_s et max_s secondes : un rythme moins régulier.

```python
random_wait(1, 3)
```

### `repeat(action, times, *args)`

**Répéter.** Appeler une fonction plusieurs fois avec les mêmes arguments. Rend la liste des résultats.

```python
repeat(tap, 5, 540, 1200)
```

### `log(message, level='INFO')`

**Écrire dans la sortie.** Afficher un message horodaté dans le panneau Sortie.

```python
log("étape 1 terminée")
```

### `input(prompt='')`

**Demander une valeur.** Poser une question dans la console du Playground et attendre la réponse.

```python
nom = input("Ton pseudo ?")
```

### `finish()`

**Terminer le script.** Arrêter le script proprement.

```python
finish()
```

## 🔎 Lire du texte dans une image (OCR)

Pour lire un texte dans un jeu (score, or, niveau) : capturer l'écran avec `frame = android_client.get_last_frame()` (image OpenCV, `frame[y1:y2, x1:x2]` pour une zone), puis lire la zone avec ocr_text.

### `ocr_text(img, psm="6", lang=None, whitelist=None, tsv=False)`

**Lire du texte (OCR).** Lit le texte d'une image avec Tesseract (embarqué dans AppsMobs). psm : "6" bloc, "7" une ligne, "8" un mot, "11" épars. lang : "fra", "eng"... whitelist : caractères permis. tsv=True : une ligne par mot avec sa boîte. Retourne la sortie brute.

```python
frame = android_client.get_last_frame()
coords = ocr_text(frame[0:60, 0:300], psm="7", whitelist="0123456789,-")
```

### `parse_number(text)`

**Nombre dans un texte.** Nombre fait de tous les chiffres du texte, ou None s'il n'y en a pas.

```python
total = parse_number('Total : 1 250 €')   # 1250
```

## Anciens noms

Ces noms marchent encore, avec leur ancien comportement, mais ne sont plus documentés. Pour un nouveau script, utiliser le nom de droite.

| Ancien nom | Nouveau nom |
|---|---|
| `any_image_exists` | `first_image` |
| `back` | `press_back` |
| `back_multiple` | `press_back` |
| `check_pixel_color` | `pixel_is` |
| `check_text_exists` | `image_visible` |
| `clear_cache` | `clear_app_data` |
| `click` | `tap` |
| `click_all` | `tap_all_images` |
| `click_first_found` | `tap_first_image` |
| `click_text` | `tap_text` |
| `click_text_partial` | `tap_text` |
| `click_until_image_appears` | `tap_until_image` |
| `click_when_visible` | `tap_image` |
| `clipboard_get` | `get_clipboard` |
| `clipboard_set` | `set_clipboard` |
| `copy_text` | `copy` |
| `doubleclick` | `double_tap` |
| `downswipe` | `swipe_down` |
| `drag_and_drop` | `drag` |
| `enter` | `press_enter` |
| `entre` | `press_enter` |
| `execute_adb` | `adb` |
| `find` | `find_image` |
| `Find` | `find_image` |
| `find_all` | `find_image_positions` |
| `find_all_images` | `find_image_positions` |
| `find_and_click` | `tap_image` |
| `find_and_double_click` | `double_tap_image` |
| `find_image_and_click` | `tap_image` |
| `find_loop` | `wait_image` |
| `FindAllImages` | `find_image_positions` |
| `Findd` | `find_image` |
| `Finddd` | `find_image` |
| `Finddoubleclick` | `double_tap_image` |
| `Findmulti` | `first_image` |
| `FindPosClick` | `tap_image` |
| `FindPosClickList` | `tap_first_image` |
| `Findtf` | `image_visible` |
| `Findtfmultiple` | `first_image` |
| `get_alarms` | `alarms` |
| `get_contacts` | `contacts` |
| `get_device_info` | `device_info` |
| `get_installed_apps` | `installed_apps` |
| `get_pixel_color` | `pixel_color` |
| `get_screen_size` | `screen_size` |
| `get_text_at_coords` | `read_text_at` |
| `get_text_from_object` | `read_text_by_id` |
| `hangup_call` | `hang_up` |
| `home` | `press_home` |
| `image_exists` | `image_visible` |
| `kill_app` | `close_app` |
| `leftswipe` | `swipe_left` |
| `long_press_when_visible` | `long_press_image` |
| `make_call` | `call` |
| `open_app_info` | `app_info` |
| `paste_text` | `paste` |
| `random_delay` | `random_wait` |
| `reboot_device` | `reboot` |
| `repeat_action` | `repeat` |
| `rightswipe` | `swipe_right` |
| `rotate_screen` | `rotate` |
| `scroll_until_visible` | `scroll_to_image` |
| `sleep` | `wait` |
| `start_activity` | `open_app` |
| `swipe_custom` | `swipe` |
| `switch` | `recent_apps` |
| `switch_app` | `recent_apps` |
| `text` | `type_text` |
| `toggle_airplane_mode` | `set_airplane_mode` |
| `toggle_data` | `set_mobile_data` |
| `toggle_wifi` | `set_wifi` |
| `upswipe` | `swipe_up` |
| `wait_for_image` | `wait_image` |
| `wait_for_text` | `wait_image` |
| `wait_until_visible` | `wait_image` |
| `wake_device` | `wake_screen` |
| `write` | `type_text` |
