zardbot-telegram 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.env.example +116 -0
  2. package/LICENSE +21 -0
  3. package/README.md +250 -0
  4. package/dist/agent/manager.js +88 -0
  5. package/dist/agent/types.js +26 -0
  6. package/dist/app/start-bot-app.js +49 -0
  7. package/dist/bot/commands/abort.js +121 -0
  8. package/dist/bot/commands/commands.js +480 -0
  9. package/dist/bot/commands/definitions.js +27 -0
  10. package/dist/bot/commands/help.js +10 -0
  11. package/dist/bot/commands/models.js +38 -0
  12. package/dist/bot/commands/new.js +70 -0
  13. package/dist/bot/commands/opencode-start.js +101 -0
  14. package/dist/bot/commands/opencode-stop.js +44 -0
  15. package/dist/bot/commands/projects.js +223 -0
  16. package/dist/bot/commands/rename.js +139 -0
  17. package/dist/bot/commands/sessions.js +351 -0
  18. package/dist/bot/commands/start.js +43 -0
  19. package/dist/bot/commands/status.js +95 -0
  20. package/dist/bot/commands/task.js +399 -0
  21. package/dist/bot/commands/tasklist.js +220 -0
  22. package/dist/bot/commands/voice.js +145 -0
  23. package/dist/bot/handlers/agent.js +118 -0
  24. package/dist/bot/handlers/context.js +100 -0
  25. package/dist/bot/handlers/document.js +65 -0
  26. package/dist/bot/handlers/inline-menu.js +119 -0
  27. package/dist/bot/handlers/model.js +143 -0
  28. package/dist/bot/handlers/permission.js +235 -0
  29. package/dist/bot/handlers/prompt.js +240 -0
  30. package/dist/bot/handlers/question.js +390 -0
  31. package/dist/bot/handlers/tts.js +89 -0
  32. package/dist/bot/handlers/variant.js +138 -0
  33. package/dist/bot/handlers/voice.js +173 -0
  34. package/dist/bot/index.js +977 -0
  35. package/dist/bot/message-patterns.js +4 -0
  36. package/dist/bot/middleware/auth.js +30 -0
  37. package/dist/bot/middleware/interaction-guard.js +95 -0
  38. package/dist/bot/middleware/unknown-command.js +22 -0
  39. package/dist/bot/streaming/response-streamer.js +286 -0
  40. package/dist/bot/streaming/tool-call-streamer.js +285 -0
  41. package/dist/bot/utils/busy-guard.js +15 -0
  42. package/dist/bot/utils/commands.js +21 -0
  43. package/dist/bot/utils/file-download.js +91 -0
  44. package/dist/bot/utils/finalize-assistant-response.js +52 -0
  45. package/dist/bot/utils/keyboard.js +69 -0
  46. package/dist/bot/utils/send-with-markdown-fallback.js +165 -0
  47. package/dist/bot/utils/telegram-text.js +28 -0
  48. package/dist/bot/utils/thinking-message.js +8 -0
  49. package/dist/cli/args.js +98 -0
  50. package/dist/cli.js +80 -0
  51. package/dist/config.js +97 -0
  52. package/dist/i18n/de.js +357 -0
  53. package/dist/i18n/en.js +357 -0
  54. package/dist/i18n/es.js +357 -0
  55. package/dist/i18n/fr.js +357 -0
  56. package/dist/i18n/index.js +109 -0
  57. package/dist/i18n/ru.js +357 -0
  58. package/dist/i18n/zh.js +357 -0
  59. package/dist/index.js +26 -0
  60. package/dist/interaction/busy.js +8 -0
  61. package/dist/interaction/cleanup.js +32 -0
  62. package/dist/interaction/guard.js +140 -0
  63. package/dist/interaction/manager.js +106 -0
  64. package/dist/interaction/types.js +1 -0
  65. package/dist/keyboard/manager.js +172 -0
  66. package/dist/keyboard/types.js +1 -0
  67. package/dist/model/capabilities.js +62 -0
  68. package/dist/model/context-limit.js +57 -0
  69. package/dist/model/manager.js +259 -0
  70. package/dist/model/types.js +24 -0
  71. package/dist/opencode/client.js +13 -0
  72. package/dist/opencode/events.js +140 -0
  73. package/dist/permission/manager.js +100 -0
  74. package/dist/permission/types.js +1 -0
  75. package/dist/pinned/format.js +29 -0
  76. package/dist/pinned/manager.js +682 -0
  77. package/dist/pinned/types.js +1 -0
  78. package/dist/process/manager.js +273 -0
  79. package/dist/process/types.js +1 -0
  80. package/dist/project/manager.js +88 -0
  81. package/dist/question/manager.js +176 -0
  82. package/dist/question/types.js +1 -0
  83. package/dist/rename/manager.js +53 -0
  84. package/dist/runtime/bootstrap.js +350 -0
  85. package/dist/runtime/mode.js +74 -0
  86. package/dist/runtime/paths.js +37 -0
  87. package/dist/scheduled-task/creation-manager.js +113 -0
  88. package/dist/scheduled-task/display.js +239 -0
  89. package/dist/scheduled-task/executor.js +87 -0
  90. package/dist/scheduled-task/foreground-state.js +32 -0
  91. package/dist/scheduled-task/next-run.js +207 -0
  92. package/dist/scheduled-task/runtime.js +368 -0
  93. package/dist/scheduled-task/schedule-parser.js +169 -0
  94. package/dist/scheduled-task/store.js +65 -0
  95. package/dist/scheduled-task/types.js +19 -0
  96. package/dist/session/cache-manager.js +455 -0
  97. package/dist/session/manager.js +10 -0
  98. package/dist/settings/manager.js +158 -0
  99. package/dist/stt/client.js +97 -0
  100. package/dist/summary/aggregator.js +1136 -0
  101. package/dist/summary/formatter.js +491 -0
  102. package/dist/summary/subagent-formatter.js +63 -0
  103. package/dist/summary/tool-message-batcher.js +90 -0
  104. package/dist/tts/client.js +130 -0
  105. package/dist/utils/error-format.js +29 -0
  106. package/dist/utils/logger.js +127 -0
  107. package/dist/utils/safe-background-task.js +33 -0
  108. package/dist/utils/telegram-rate-limit-retry.js +93 -0
  109. package/dist/variant/manager.js +103 -0
  110. package/dist/variant/types.js +1 -0
  111. package/package.json +79 -0
@@ -0,0 +1,357 @@
1
+ export const fr = {
2
+ "cmd.description.status": "Statut du serveur et de la session",
3
+ "cmd.description.new": "Créer une nouvelle session",
4
+ "cmd.description.stop": "Arrêter l'action en cours",
5
+ "cmd.description.sessions": "Lister les sessions",
6
+ "cmd.description.projects": "Lister les projets",
7
+ "cmd.description.task": "Créer une tâche planifiée",
8
+ "cmd.description.tasklist": "Afficher les tâches planifiées",
9
+ "cmd.description.commands": "Commandes personnalisées",
10
+ "cmd.description.voice": "Sélectionner la voix TTS",
11
+ "cmd.description.opencode_start": "Démarrer le serveur OpenCode",
12
+ "cmd.description.opencode_stop": "Arrêter le serveur OpenCode",
13
+ "cmd.description.help": "Aide",
14
+ "callback.unknown_command": "Commande inconnue",
15
+ "callback.processing_error": "Erreur de traitement",
16
+ "error.load_agents": "❌ Impossible de charger la liste des modes",
17
+ "error.load_models": "❌ Impossible de charger la liste des modèles",
18
+ "error.load_variants": "❌ Impossible de charger la liste des variantes",
19
+ "error.context_button": "❌ Impossible de traiter le bouton de contexte",
20
+ "error.generic": "🔴 Une erreur s'est produite.",
21
+ "interaction.blocked.expired": "⚠️ Cette interaction a expiré. Veuillez la relancer.",
22
+ "interaction.blocked.expected_callback": "⚠️ Veuillez utiliser les boutons inline pour cette étape ou appuyer sur Annuler.",
23
+ "interaction.blocked.expected_text": "⚠️ Veuillez envoyer un message texte pour cette étape.",
24
+ "interaction.blocked.expected_command": "⚠️ Veuillez envoyer une commande pour cette étape.",
25
+ "interaction.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible à l'étape actuelle.",
26
+ "interaction.blocked.finish_current": "⚠️ Terminez d'abord l'interaction en cours (réponse ou annulation), puis ouvrez un autre menu.",
27
+ "inline.blocked.expected_choice": "⚠️ Choisissez une option avec les boutons inline ou appuyez sur Annuler.",
28
+ "inline.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible tant que le menu inline est actif.",
29
+ "question.blocked.expected_answer": "⚠️ Répondez à la question en cours avec les boutons, Réponse personnalisée ou Annuler.",
30
+ "question.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible tant que le flux de question actuel n'est pas terminé.",
31
+ "inline.button.cancel": "❌ Annuler",
32
+ "inline.inactive_callback": "Ce menu est inactif",
33
+ "inline.cancelled_callback": "Annulé",
34
+ "common.unknown": "inconnu",
35
+ "common.unknown_error": "erreur inconnue",
36
+ "start.welcome": "👋 Bienvenue dans OpenCode Telegram Bot !\n\nUtilisez les commandes :\n/projects — sélectionner un projet\n/sessions — liste des sessions\n/new — nouvelle session\n/task — tâche planifiée\n/tasklist — tâches planifiées\n/status — statut\n/help — aide\n\nUtilisez les boutons du bas pour choisir le mode d'agent, le modèle et la variante.",
37
+ "help.keyboard_hint": "💡 Utilisez les boutons du bas pour le mode d'agent, le modèle, la variante et les actions de contexte.",
38
+ "help.text": "📖 **Aide**\n\n/status - Vérifier l'état du serveur\n/sessions - Liste des sessions\n/new - Créer une nouvelle session\n/help - Aide",
39
+ "bot.thinking": "💭 Réflexion en cours...",
40
+ "bot.project_not_selected": "🏗 Aucun projet n'est sélectionné.\n\nSélectionnez d'abord un projet avec /projects.",
41
+ "bot.creating_session": "🔄 Création d'une nouvelle session...",
42
+ "bot.create_session_error": "🔴 Impossible de créer la session. Essayez /new ou vérifiez l'état du serveur avec /status.",
43
+ "bot.session_created": "✅ Session créée : {title}",
44
+ "bot.session_busy": "⏳ L'agent exécute déjà une tâche. Attendez la fin ou utilisez /abort pour interrompre l'exécution en cours.",
45
+ "bot.session_reset_project_mismatch": "⚠️ La session active ne correspond pas au projet sélectionné, elle a donc été réinitialisée. Utilisez /sessions pour en choisir une ou /new pour créer une nouvelle session.",
46
+ "bot.prompt_send_error": "Impossible d'envoyer la requête à OpenCode.",
47
+ "bot.session_error": "🔴 OpenCode a renvoyé une erreur : {message}",
48
+ "bot.session_retry": "🔁 {message}\n\nLe fournisseur renvoie la même erreur à chaque nouvelle tentative. Utilisez /abort pour arrêter.",
49
+ "bot.unknown_command": "⚠️ Commande inconnue : {command}. Utilisez /help pour voir les commandes disponibles.",
50
+ "bot.photo_downloading": "⏳ Téléchargement de la photo...",
51
+ "bot.photo_too_large": "⚠️ La photo est trop volumineuse (max {maxSizeMb}MB)",
52
+ "bot.photo_model_no_image": "⚠️ Le modèle actuel ne prend pas en charge les images. Envoi du texte uniquement.",
53
+ "bot.photo_download_error": "🔴 Impossible de télécharger la photo",
54
+ "bot.photo_no_caption": "💡 Conseil : ajoutez une légende pour décrire ce que vous voulez faire avec cette photo.",
55
+ "bot.file_downloading": "⏳ Téléchargement du fichier...",
56
+ "bot.file_too_large": "⚠️ Le fichier est trop volumineux (max {maxSizeMb}MB)",
57
+ "bot.file_download_error": "🔴 Impossible de télécharger le fichier",
58
+ "bot.model_no_pdf": "⚠️ Le modèle actuel ne prend pas en charge les PDF. Envoi du texte uniquement.",
59
+ "bot.text_file_too_large": "⚠️ Le fichier texte est trop volumineux (max {maxSizeKb}KB)",
60
+ "status.header_running": "🟢 Le serveur OpenCode est en cours d'exécution",
61
+ "status.health.healthy": "Sain",
62
+ "status.health.unhealthy": "Dégradé",
63
+ "status.line.health": "Statut : {health}",
64
+ "status.line.version": "Version : {version}",
65
+ "status.line.managed_yes": "Démarré par le bot : Oui",
66
+ "status.line.managed_no": "Démarré par le bot : Non",
67
+ "status.line.pid": "PID : {pid}",
68
+ "status.line.uptime_sec": "Temps de fonctionnement : {seconds} sec",
69
+ "status.line.mode": "Mode : {mode}",
70
+ "status.line.model": "Modèle : {model}",
71
+ "status.agent_not_set": "non défini",
72
+ "status.project_selected": "Projet : {project}",
73
+ "status.project_not_selected": "Projet : non sélectionné",
74
+ "status.project_hint": "Utilisez /projects pour sélectionner un projet",
75
+ "status.session_selected": "Session actuelle : {title}",
76
+ "status.session_not_selected": "Session actuelle : non sélectionnée",
77
+ "status.session_hint": "Utilisez /sessions pour en sélectionner une ou /new pour en créer une",
78
+ "status.server_unavailable": "🔴 Le serveur OpenCode est indisponible\n\nUtilisez /opencode_start pour démarrer le serveur.",
79
+ "projects.empty": "📭 Aucun projet trouvé.\n\nOuvrez un répertoire dans OpenCode et créez au moins une session, il apparaîtra ensuite ici.",
80
+ "projects.select": "Sélectionnez un projet :",
81
+ "projects.select_with_current": "Sélectionnez un projet :\n\nActuel : 🏗 {project}",
82
+ "projects.page_indicator": "Page {current}/{total}",
83
+ "projects.prev_page": "⬅️ Précédent",
84
+ "projects.next_page": "Suivant ➡️",
85
+ "projects.fetch_error": "🔴 Le serveur OpenCode est indisponible ou une erreur s'est produite lors du chargement des projets.",
86
+ "projects.page_load_error": "Impossible de charger cette page. Veuillez réessayer.",
87
+ "projects.selected": "✅ Projet sélectionné : {project}\n\n📋 La session a été réinitialisée. Utilisez /sessions ou /new pour ce projet.",
88
+ "projects.select_error": "🔴 Impossible de sélectionner le projet.",
89
+ "sessions.project_not_selected": "🏗 Aucun projet n'est sélectionné.\n\nSélectionnez d'abord un projet avec /projects.",
90
+ "sessions.empty": "📭 Aucune session trouvée.\n\nCréez une nouvelle session avec /new.",
91
+ "sessions.select": "Sélectionnez une session :",
92
+ "sessions.select_page": "Sélectionnez une session (page {page}) :",
93
+ "sessions.fetch_error": "🔴 Le serveur OpenCode est indisponible ou une erreur s'est produite lors du chargement des sessions.",
94
+ "sessions.select_project_first": "🔴 Aucun projet n'est sélectionné. Utilisez /projects.",
95
+ "sessions.page_empty_callback": "Aucune session sur cette page",
96
+ "sessions.page_load_error_callback": "Impossible de charger cette page. Veuillez réessayer.",
97
+ "sessions.button.prev_page": "⬅️ Préc.",
98
+ "sessions.button.next_page": "Suiv. ➡️",
99
+ "sessions.loading_context": "⏳ Chargement du contexte et des derniers messages...",
100
+ "sessions.selected": "✅ Session sélectionnée : {title}",
101
+ "sessions.select_error": "🔴 Impossible de sélectionner la session.",
102
+ "sessions.preview.empty": "Aucun message récent.",
103
+ "sessions.preview.title": "Messages récents :",
104
+ "sessions.preview.you": "Vous :",
105
+ "sessions.preview.agent": "Agent :",
106
+ "new.project_not_selected": "🏗 Aucun projet n'est sélectionné.\n\nSélectionnez d'abord un projet avec /projects.",
107
+ "new.created": "✅ Nouvelle session créée : {title}",
108
+ "new.create_error": "🔴 Le serveur OpenCode est indisponible ou une erreur s'est produite lors de la création de la session.",
109
+ "stop.no_active_session": "🛑 L'agent n'a pas été démarré\n\nCréez une session avec /new ou sélectionnez-en une via /sessions.",
110
+ "stop.in_progress": "🛑 Flux d'événements arrêté, envoi du signal d'abandon...\n\nEn attente de l'arrêt de l'agent.",
111
+ "stop.warn_unconfirmed": "⚠️ Le flux d'événements a été arrêté, mais le serveur n'a pas confirmé l'abandon.\n\nVérifiez /status et réessayez /abort dans quelques secondes.",
112
+ "stop.warn_maybe_finished": "⚠️ Le flux d'événements a été arrêté, mais l'agent a peut-être déjà terminé.",
113
+ "stop.success": "✅ Action de l'agent interrompue. Aucun autre message de cette exécution ne sera envoyé.",
114
+ "stop.warn_still_busy": "⚠️ Le signal a été envoyé, mais l'agent est toujours occupé.\n\nLe flux d'événements est déjà désactivé, donc aucun message intermédiaire ne sera envoyé.",
115
+ "stop.warn_timeout": "⚠️ Délai dépassé pour la requête d'abandon.\n\nLe flux d'événements est déjà arrêté, réessayez /abort dans quelques secondes.",
116
+ "stop.warn_local_only": "⚠️ Le flux d'événements a été arrêté localement, mais l'abandon côté serveur a échoué.",
117
+ "stop.error": "🔴 Impossible d'arrêter l'action.\n\nLe flux d'événements est arrêté, essayez /abort à nouveau.",
118
+ "opencode_start.already_running_managed": "⚠️ Le serveur OpenCode est déjà en cours d'exécution\n\nPID : {pid}\nTemps de fonctionnement : {seconds} secondes",
119
+ "opencode_start.already_running_external": "✅ Le serveur OpenCode est déjà en cours d'exécution en tant que processus externe\n\nVersion : {version}\n\nCe serveur n'a pas été démarré par le bot, donc /opencode-stop ne peut pas l'arrêter.",
120
+ "opencode_start.starting": "🔄 Démarrage du serveur OpenCode...",
121
+ "opencode_start.start_error": "🔴 Impossible de démarrer le serveur OpenCode\n\nErreur : {error}\n\nVérifiez que l'interface en ligne de commande OpenCode est installée et disponible dans le PATH :\nopencode --version\nnpm install -g @opencode-ai/cli",
122
+ "opencode_start.started_not_ready": "⚠️ Le serveur OpenCode a démarré, mais ne répond pas encore\n\nPID : {pid}\n\nLe serveur est peut-être encore en cours de démarrage. Essayez /status dans quelques secondes.",
123
+ "opencode_start.success": "✅ Serveur OpenCode démarré avec succès\n\nPID : {pid}\nVersion : {version}",
124
+ "opencode_start.error": "🔴 Une erreur s'est produite lors du démarrage du serveur.\n\nConsultez les logs de l'application pour plus de détails.",
125
+ "opencode_stop.external_running": "⚠️ Le serveur OpenCode s'exécute comme processus externe\n\nCe serveur n'a pas été démarré via /opencode-start.\nArrêtez-le manuellement ou utilisez /status pour vérifier son état.",
126
+ "opencode_stop.not_running": "⚠️ Le serveur OpenCode n'est pas en cours d'exécution",
127
+ "opencode_stop.stopping": "🛑 Arrêt du serveur OpenCode...\n\nPID : {pid}",
128
+ "opencode_stop.stop_error": "🔴 Impossible d'arrêter le serveur OpenCode\n\nErreur : {error}",
129
+ "opencode_stop.success": "✅ Serveur OpenCode arrêté avec succès",
130
+ "opencode_stop.error": "🔴 Une erreur s'est produite lors de l'arrêt du serveur.\n\nConsultez les logs de l'application pour plus de détails.",
131
+ "agent.changed_callback": "Mode modifié : {name}",
132
+ "agent.changed_message": "✅ Mode défini sur : {name}",
133
+ "agent.change_error_callback": "Impossible de modifier le mode",
134
+ "agent.menu.current": "Mode actuel : {name}\n\nSélectionnez un mode :",
135
+ "agent.menu.select": "Sélectionnez un mode de travail :",
136
+ "agent.menu.empty": "⚠️ Aucun mode disponible",
137
+ "agent.menu.error": "🔴 Impossible de récupérer la liste des modes",
138
+ "model.changed_callback": "Modèle modifié : {name}",
139
+ "model.changed_message": "✅ Modèle défini sur : {name}",
140
+ "model.change_error_callback": "Impossible de modifier le modèle",
141
+ "model.menu.empty": "⚠️ Aucun modèle disponible",
142
+ "model.menu.select": "Sélectionnez un modèle :",
143
+ "model.menu.current": "Modèle actuel : {name}\n\nSélectionnez un modèle :",
144
+ "model.menu.favorites_title": "⭐ Favoris (ajoutez des modèles aux favoris dans l'interface OpenCode)",
145
+ "model.menu.favorites_empty": "— Vide.",
146
+ "model.menu.recent_title": "🕘 Récents",
147
+ "model.menu.recent_empty": "— Vide.",
148
+ "model.menu.favorites_hint": "ℹ️ Ajoutez des modèles aux favoris dans l'interface OpenCode pour les garder en tête de liste.",
149
+ "model.menu.error": "🔴 Impossible de récupérer la liste des modèles",
150
+ "variant.model_not_selected_callback": "Erreur : aucun modèle sélectionné",
151
+ "variant.changed_callback": "Variante modifiée : {name}",
152
+ "variant.changed_message": "✅ Variante définie sur : {name}",
153
+ "variant.change_error_callback": "Impossible de modifier la variante",
154
+ "variant.select_model_first": "⚠️ Sélectionnez d'abord un modèle",
155
+ "variant.menu.empty": "⚠️ Aucune variante disponible",
156
+ "variant.menu.current": "Variante actuelle : {name}\n\nSélectionnez une variante :",
157
+ "variant.menu.error": "🔴 Impossible de récupérer la liste des variantes",
158
+ "context.button.confirm": "✅ Oui, compacter le contexte",
159
+ "context.no_active_session": "⚠️ Aucune session active. Créez une session avec /new",
160
+ "context.confirm_text": "📊 Réduction du contexte pour la session \"{title}\"\n\nCela réduira l'utilisation du contexte en supprimant les anciens messages de l'historique. La tâche en cours ne sera pas interrompue.\n\nContinuer ?",
161
+ "context.callback_session_not_found": "Session introuvable",
162
+ "context.callback_compacting": "Réduction du contexte en cours...",
163
+ "context.progress": "⏳ Réduction du contexte en cours...",
164
+ "context.error": "❌ La réduction du contexte a échoué",
165
+ "context.success": "✅ Contexte compacté avec succès",
166
+ "permission.inactive_callback": "La demande d'autorisation est inactive",
167
+ "permission.processing_error_callback": "Erreur de traitement",
168
+ "permission.no_active_request_callback": "Erreur : aucune demande active",
169
+ "permission.reply.once": "Autorisé une fois",
170
+ "permission.reply.always": "Toujours autorisé",
171
+ "permission.reply.reject": "Refusé",
172
+ "permission.send_reply_error": "❌ Impossible d'envoyer la réponse d'autorisation",
173
+ "permission.blocked.expected_reply": "⚠️ Veuillez d'abord répondre à la demande d'autorisation avec les boutons ci-dessus.",
174
+ "permission.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible tant que vous n'avez pas répondu à la demande d'autorisation.",
175
+ "permission.header": "{emoji} Demande d'autorisation : {name}\n\n",
176
+ "permission.button.allow": "✅ Autoriser une fois",
177
+ "permission.button.always": "🔓 Toujours autoriser",
178
+ "permission.button.reject": "❌ Refuser",
179
+ "permission.name.bash": "Bash",
180
+ "permission.name.edit": "Modifier",
181
+ "permission.name.write": "Écrire",
182
+ "permission.name.read": "Lire",
183
+ "permission.name.webfetch": "Récupération web",
184
+ "permission.name.websearch": "Recherche web",
185
+ "permission.name.glob": "Recherche de fichiers",
186
+ "permission.name.grep": "Recherche de contenu",
187
+ "permission.name.list": "Lister le répertoire",
188
+ "permission.name.task": "Tâche",
189
+ "permission.name.lsp": "LSP",
190
+ "permission.name.external_directory": "Répertoire externe",
191
+ "question.inactive_callback": "Le sondage est inactif",
192
+ "question.processing_error_callback": "Erreur de traitement",
193
+ "question.select_one_required_callback": "Sélectionnez au moins une option",
194
+ "question.enter_custom_callback": "Envoyez votre réponse personnalisée sous forme de message",
195
+ "question.cancelled": "❌ Sondage annulé",
196
+ "question.answer_already_received": "Réponse déjà reçue, veuillez patienter...",
197
+ "question.completed_no_answers": "✅ Sondage terminé (aucune réponse)",
198
+ "question.no_active_project": "❌ Aucun projet actif",
199
+ "question.no_active_request": "❌ Aucune demande active",
200
+ "question.send_answers_error": "❌ Impossible d'envoyer les réponses à l'agent",
201
+ "question.multi_hint": "\n(Vous pouvez sélectionner plusieurs options)",
202
+ "question.button.submit": "✅ Terminer",
203
+ "question.button.custom": "🔤 Réponse personnalisée",
204
+ "question.button.cancel": "❌ Annuler",
205
+ "question.use_custom_button_first": "⚠️ Pour envoyer du texte, appuyez d'abord sur « Réponse personnalisée » pour la question actuelle.",
206
+ "question.summary.title": "✅ Sondage terminé !\n\n",
207
+ "question.summary.question": "Question {index} :\n{question}\n\n",
208
+ "question.summary.answer": "Réponse :\n{answer}\n\n",
209
+ "keyboard.agent_mode": "{emoji} Mode {name}",
210
+ "keyboard.context": "📊 {used} / {limit} ({percent}%)",
211
+ "keyboard.context_empty": "📊 0",
212
+ "keyboard.variant": "💭 {name}",
213
+ "keyboard.variant_default": "💡 Par défaut",
214
+ "keyboard.updated": "⌨️ Clavier mis à jour",
215
+ "pinned.default_session_title": "nouvelle session",
216
+ "pinned.unknown": "Inconnu",
217
+ "pinned.line.project": "Projet : {project}",
218
+ "pinned.line.model": "Modèle : {model}",
219
+ "pinned.line.context": "Contexte : {used} / {limit} ({percent}%)",
220
+ "pinned.line.cost": "Coût : {cost} dépensé",
221
+ "subagent.header": "Sous-agent {agent} : {description}",
222
+ "subagent.line.status": "Statut : {status}",
223
+ "subagent.line.task": "Tache : {task}",
224
+ "subagent.line.agent": "Agent : {agent}",
225
+ "subagent.working": "En cours...",
226
+ "subagent.working_with_details": "En cours : {details}",
227
+ "subagent.completed": "Terminee",
228
+ "subagent.failed": "Echec de la tache",
229
+ "subagent.status.pending": "en attente",
230
+ "subagent.status.running": "en cours",
231
+ "subagent.status.completed": "termine",
232
+ "subagent.status.error": "erreur",
233
+ "pinned.files.title": "Fichiers ({count}) :",
234
+ "pinned.files.item": " {path}{diff}",
235
+ "pinned.files.more": " ... et encore {count}",
236
+ "tool.todo.overflow": "*({count} tâches supplémentaires)*",
237
+ "tool.file_header.write": "Écrire Fichier/Chemin : {path}\n============================================================\n\n",
238
+ "tool.file_header.edit": "Modifier Fichier/Chemin : {path}\n============================================================\n\n",
239
+ "runtime.wizard.ask_token": "Entrez le token du bot Telegram (obtenez-le auprès de @BotFather).\n> ",
240
+ "runtime.wizard.ask_language": "Sélectionnez la langue de l'interface.\nEntrez le numéro de la langue dans la liste ou le code locale.\nAppuyez sur Entrée pour conserver la langue par défaut : {defaultLocale}\n{options}\n> ",
241
+ "runtime.wizard.language_invalid": "Entrez un numéro de langue de la liste ou un code locale pris en charge.\n",
242
+ "runtime.wizard.language_selected": "Langue sélectionnée : {language}\n",
243
+ "runtime.wizard.token_required": "Le token est requis. Veuillez réessayer.\n",
244
+ "runtime.wizard.token_invalid": "Le token semble invalide (format attendu <id>:<secret>). Veuillez réessayer.\n",
245
+ "runtime.wizard.ask_user_id": "Entrez votre identifiant utilisateur Telegram (vous pouvez l'obtenir auprès de @userinfobot).\n> ",
246
+ "runtime.wizard.user_id_invalid": "Entrez un entier positif (> 0).\n",
247
+ "runtime.wizard.ask_api_url": "Entrez l'URL de l'API OpenCode (optionnel).\nAppuyez sur Entrée pour utiliser la valeur par défaut : {defaultUrl}\n> ",
248
+ "runtime.wizard.ask_server_username": "Entrez le nom d'utilisateur du serveur OpenCode (optionnel).\nAppuyez sur Entrée pour utiliser la valeur par défaut : {defaultUsername}\n> ",
249
+ "runtime.wizard.ask_server_password": "Entrez le mot de passe du serveur OpenCode (optionnel).\nAppuyez sur Entrée pour le laisser vide.\n> ",
250
+ "runtime.wizard.api_url_invalid": "Entrez une URL valide (http/https) ou appuyez sur Entrée pour la valeur par défaut.\n",
251
+ "runtime.wizard.start": "Configuration d'OpenCode Telegram Bot.\n",
252
+ "runtime.wizard.saved": "Configuration enregistrée :\n- {envPath}\n- {settingsPath}\n",
253
+ "runtime.wizard.not_configured_starting": "L'application n'est pas encore configurée. Lancement de l'assistant...\n",
254
+ "runtime.wizard.tty_required": "L'assistant interactif nécessite un terminal TTY. Exécutez `opencode-telegram config` dans un shell interactif.",
255
+ "rename.no_session": "⚠️ Aucune session active. Créez ou sélectionnez d'abord une session.",
256
+ "rename.prompt": "📝 Entrez le nouveau titre de la session :\n\nActuel : {title}",
257
+ "rename.empty_title": "⚠️ Le titre ne peut pas être vide.",
258
+ "rename.success": "✅ Session renommée en : {title}",
259
+ "rename.error": "🔴 Impossible de renommer la session.",
260
+ "rename.cancelled": "❌ Renommage annulé.",
261
+ "rename.inactive_callback": "La demande de renommage est inactive",
262
+ "rename.inactive": "⚠️ La demande de renommage n'est pas active. Exécutez /rename à nouveau.",
263
+ "rename.blocked.expected_name": "⚠️ Entrez le nouveau nom de la session sous forme de texte ou appuyez sur Annuler dans le message de renommage.",
264
+ "rename.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible tant que le renommage attend un nouveau nom.",
265
+ "rename.button.cancel": "❌ Annuler",
266
+ "task.prompt.schedule": "⏰ Envoyez le planning de la tâche en langage naturel.\n\nExemples :\n- toutes les 5 minutes\n- chaque jour à 17:00\n- demain à 12:00",
267
+ "task.schedule_empty": "⚠️ Le planning ne peut pas être vide.",
268
+ "task.parse.in_progress": "⏳ Analyse du planning...",
269
+ "task.parse_error": "🔴 Impossible d'interpréter le planning.\n\n{message}\n\nEnvoyez le créneau à nouveau de façon plus claire.",
270
+ "task.schedule_preview": "✅ Planning interprété\n\nCompris comme : {summary}\n{cronLine}Fuseau horaire : {timezone}\nType : {kind}\nProchaine exécution : {nextRunAt}",
271
+ "task.schedule_preview.cron": "Cron : {cron}",
272
+ "task.prompt.body": "📝 Envoyez maintenant ce que le bot doit faire selon ce planning.",
273
+ "task.prompt_empty": "⚠️ Le texte de la tâche ne peut pas être vide.",
274
+ "task.created": "✅ Tâche planifiée créée\n\nTâche : {description}\nProjet : {project}\nModèle : {model}\nPlanning : {schedule}\n{cronLine}Prochaine exécution : {nextRunAt}",
275
+ "task.created.cron": "Cron : {cron}",
276
+ "task.button.retry_schedule": "🔁 Ressaisir le planning",
277
+ "task.button.cancel": "❌ Annuler",
278
+ "task.retry_schedule_callback": "Retour à la saisie du planning...",
279
+ "task.cancel_callback": "Annulation...",
280
+ "task.cancelled": "❌ Création de la tâche planifiée annulée.",
281
+ "task.inactive_callback": "Ce flux de tâche planifiée n'est plus actif",
282
+ "task.inactive": "⚠️ La création de tâche planifiée n'est pas active. Relancez /task.",
283
+ "task.blocked.expected_input": "⚠️ Terminez d'abord la configuration de la tâche planifiée : envoyez du texte ou utilisez le bouton dans le message du planning.",
284
+ "task.blocked.command_not_allowed": "⚠️ Cette commande n'est pas disponible pendant la création d'une tâche planifiée.",
285
+ "task.limit_reached": "⚠️ Limite de tâches atteinte ({limit}). Supprimez d'abord une tâche planifiée existante.",
286
+ "task.schedule_too_frequent": "Le planning récurrent est trop fréquent. L'intervalle minimum autorisé est d'une fois toutes les 5 minutes.",
287
+ "task.kind.cron": "récurrente",
288
+ "task.kind.once": "ponctuelle",
289
+ "task.run.success": "⏰ Tâche planifiée terminée : {description}",
290
+ "task.run.error": "🔴 Échec de la tâche planifiée : {description}\n\nErreur : {error}",
291
+ "tasklist.empty": "📭 Aucune tâche planifiée pour le moment.",
292
+ "tasklist.select": "Sélectionnez une tâche planifiée :",
293
+ "tasklist.details": "⏰ Tâche planifiée\n\nTâche : {prompt}\nProjet : {project}\nPlanning : {schedule}\n{cronLine}Fuseau horaire : {timezone}\nProchaine exécution : {nextRunAt}\nDernière exécution : {lastRunAt}\nNombre d'exécutions : {runCount}",
294
+ "tasklist.details.cron": "Cron : {cron}",
295
+ "tasklist.button.delete": "🗑 Supprimer",
296
+ "tasklist.button.cancel": "❌ Annuler",
297
+ "tasklist.deleted_callback": "Supprimée",
298
+ "tasklist.cancelled_callback": "Annulé",
299
+ "tasklist.inactive_callback": "Ce menu des tâches planifiées est inactif",
300
+ "tasklist.load_error": "🔴 Impossible de charger les tâches planifiées.",
301
+ "commands.select": "Choisissez une commande OpenCode :",
302
+ "commands.empty": "📭 Aucune commande OpenCode n'est disponible pour ce projet.",
303
+ "commands.fetch_error": "🔴 Impossible de charger les commandes OpenCode.",
304
+ "commands.no_description": "Aucune description",
305
+ "commands.button.execute": "✅ Exécuter",
306
+ "commands.button.cancel": "❌ Annuler",
307
+ "commands.confirm": "Confirmez l'exécution de la commande {command}. Pour l'exécuter avec des arguments, envoyez-les dans un message.",
308
+ "commands.inactive_callback": "Ce menu de commandes est inactif",
309
+ "commands.cancelled_callback": "Annulé",
310
+ "commands.execute_callback": "Exécution de la commande...",
311
+ "commands.executing_prefix": "⚡ Exécution de la commande :",
312
+ "commands.arguments_empty": "⚠️ Les arguments ne peuvent pas être vides. Envoyez du texte ou appuyez sur Exécuter.",
313
+ "commands.execute_error": "🔴 Impossible d'exécuter la commande OpenCode.",
314
+ "commands.select_page": "Choisissez une commande OpenCode (page {page}) :",
315
+ "commands.button.prev_page": "⬅️ Précédent",
316
+ "commands.button.next_page": "Suivant ➡️",
317
+ "commands.page_empty_callback": "Aucune commande sur cette page",
318
+ "commands.page_load_error_callback": "Impossible de charger cette page. Veuillez réessayer.",
319
+ "cmd.description.rename": "Renommer la session actuelle",
320
+ "cli.usage": "Utilisation :\n opencode-telegram [start] [--mode sources|installed]\n opencode-telegram status\n opencode-telegram stop\n opencode-telegram config\n\nNotes :\n - Sans commande, `start` est utilisé par défaut\n - `--mode` n'est actuellement pris en charge que pour `start`",
321
+ "cli.placeholder.status": "La commande `status` est actuellement un placeholder. Les vraies vérifications d'état seront ajoutées dans la couche service (Phase 5).",
322
+ "cli.placeholder.stop": "La commande `stop` est actuellement un placeholder. Le véritable arrêt du processus en arrière-plan sera ajouté dans la couche service (Phase 5).",
323
+ "cli.placeholder.unavailable": "Commande indisponible.",
324
+ "cli.error.prefix": "Erreur CLI : {message}",
325
+ "cli.args.unknown_command": "Commande inconnue : {value}",
326
+ "cli.args.mode_requires_value": "L'option --mode nécessite une valeur : sources|installed",
327
+ "cli.args.invalid_mode": "Valeur de mode invalide : {value}. Attendu : sources|installed",
328
+ "cli.args.unknown_option": "Option inconnue : {value}",
329
+ "cli.args.mode_only_start": "L'option --mode est prise en charge uniquement pour la commande start",
330
+ "legacy.models.fetch_error": "🔴 Impossible de récupérer la liste des modèles. Vérifiez l'état du serveur avec /status.",
331
+ "legacy.models.empty": "📋 Aucun modèle disponible. Configurez les fournisseurs dans OpenCode.",
332
+ "legacy.models.header": "📋 Modèles disponibles :\n\n",
333
+ "legacy.models.no_provider_models": " ⚠️ Aucun modèle disponible\n",
334
+ "legacy.models.env_hint": "💡 Pour utiliser le modèle dans .env :\n",
335
+ "legacy.models.error": "🔴 Une erreur s'est produite lors du chargement de la liste des modèles.",
336
+ "stt.recognizing": "🎤 Reconnaissance audio en cours...",
337
+ "stt.recognized": "🎤 Reconnu :\n{text}",
338
+ "stt.not_configured": "🎤 La reconnaissance vocale n'est pas configurée.\n\nDéfinissez STT_API_URL et STT_API_KEY dans .env pour l'activer.",
339
+ "stt.error": "🔴 Impossible de reconnaître l'audio : {error}",
340
+ "stt.empty_result": "🎤 Aucune parole détectée dans le message audio.",
341
+ "tts.not_configured": "🔊 La synthèse vocale n'est pas configurée.\n\nDéfinissez TTS_API_URL dans .env pour l'activer.\n\nRecommandé : Utilisez pocket-tts-server pour le TTS local.\nhttps://github.com/ai-joe-git/pocket-tts-server",
342
+ "tts.voice_changed": "🔊 Voix changée : {voice}",
343
+ "tts.voice_error": "🔴 Impossible de changer la voix : {error}",
344
+ "tts.fetch_voices_error": "🔴 Impossible de récupérer les voix du serveur TTS.",
345
+ "tts.synthesis_error": "🔴 Impossible de synthétiser la parole : {error}",
346
+ "tts.speaking": "🔊 En cours de lecture...",
347
+ "tts.menu_title": "Sélectionnez une voix :",
348
+ "tts.menu_current": "Voix actuelle : {voice}\n\nSélectionnez une voix :",
349
+ "tts.menu_empty": "⚠️ Aucune voix disponible. Vérifiez la configuration du serveur TTS.",
350
+ "tts.menu_loading": "⏳ Chargement des voix...",
351
+ "tts.menu_error": "🔴 Impossible de charger les voix.",
352
+ "tts.button.off": "🔇 TTS Désactivé",
353
+ "tts.button.off_description": "Désactiver les réponses vocales",
354
+ "tts.button.prev_page": "⬅️ Précédent",
355
+ "tts.button.next_page": "Suivant ➡️",
356
+ "tts.off_success": "🔇 Réponses vocales désactivées.",
357
+ };
@@ -0,0 +1,109 @@
1
+ import { en } from "./en.js";
2
+ import { de } from "./de.js";
3
+ import { es } from "./es.js";
4
+ import { fr } from "./fr.js";
5
+ import { ru } from "./ru.js";
6
+ import { zh } from "./zh.js";
7
+ const LOCALE_DEFINITIONS = [
8
+ {
9
+ code: "en",
10
+ label: "English",
11
+ dateLocale: "en-US",
12
+ dictionary: en,
13
+ },
14
+ {
15
+ code: "de",
16
+ label: "Deutsch",
17
+ dateLocale: "de-DE",
18
+ dictionary: de,
19
+ },
20
+ {
21
+ code: "es",
22
+ label: "Español",
23
+ dateLocale: "es-ES",
24
+ dictionary: es,
25
+ },
26
+ {
27
+ code: "fr",
28
+ label: "Français",
29
+ dateLocale: "fr-FR",
30
+ dictionary: fr,
31
+ },
32
+ {
33
+ code: "ru",
34
+ label: "Русский",
35
+ dateLocale: "ru-RU",
36
+ dictionary: ru,
37
+ },
38
+ {
39
+ code: "zh",
40
+ label: "简体中文",
41
+ dateLocale: "zh-CN",
42
+ dictionary: zh,
43
+ },
44
+ ];
45
+ const DEFAULT_LOCALE = "en";
46
+ export const SUPPORTED_LOCALES = LOCALE_DEFINITIONS.map((definition) => definition.code);
47
+ const localeDefinitionByCode = Object.fromEntries(LOCALE_DEFINITIONS.map((definition) => [definition.code, definition]));
48
+ let runtimeLocaleOverride = null;
49
+ export function resolveSupportedLocale(locale) {
50
+ const normalized = locale?.trim().toLowerCase();
51
+ if (!normalized) {
52
+ return null;
53
+ }
54
+ if (Object.hasOwn(localeDefinitionByCode, normalized)) {
55
+ return normalized;
56
+ }
57
+ const baseLocale = normalized.split("-")[0];
58
+ if (Object.hasOwn(localeDefinitionByCode, baseLocale)) {
59
+ return baseLocale;
60
+ }
61
+ return null;
62
+ }
63
+ export function normalizeLocale(locale, fallback = DEFAULT_LOCALE) {
64
+ return resolveSupportedLocale(locale) ?? fallback;
65
+ }
66
+ export function isSupportedLocale(locale) {
67
+ return resolveSupportedLocale(locale) !== null;
68
+ }
69
+ export function getLocaleOptions() {
70
+ return LOCALE_DEFINITIONS.map(({ code, label }) => ({ code, label }));
71
+ }
72
+ export function getDateLocale(locale) {
73
+ const activeLocale = locale ?? getLocale();
74
+ return localeDefinitionByCode[activeLocale].dateLocale;
75
+ }
76
+ function interpolate(template, params) {
77
+ if (!params) {
78
+ return template;
79
+ }
80
+ return template.replace(/\{([a-zA-Z0-9_]+)\}/g, (fullMatch, key) => {
81
+ const value = params[key];
82
+ if (value === undefined || value === null) {
83
+ return fullMatch;
84
+ }
85
+ return String(value);
86
+ });
87
+ }
88
+ export function getLocale() {
89
+ if (runtimeLocaleOverride) {
90
+ return runtimeLocaleOverride;
91
+ }
92
+ const localeFromEnv = process.env.BOT_LOCALE;
93
+ return normalizeLocale(localeFromEnv, DEFAULT_LOCALE);
94
+ }
95
+ export function setRuntimeLocale(locale) {
96
+ runtimeLocaleOverride = locale;
97
+ }
98
+ export function resetRuntimeLocale() {
99
+ runtimeLocaleOverride = null;
100
+ }
101
+ export function t(key, params, locale) {
102
+ const activeLocale = locale ?? getLocale();
103
+ const dictionary = localeDefinitionByCode[activeLocale].dictionary;
104
+ const template = dictionary[key] ?? en[key];
105
+ if (!template) {
106
+ return key;
107
+ }
108
+ return interpolate(template, params);
109
+ }