optimize: channel Message Commands — Multilingual Support and Prefixes
This commit is contained in:
@@ -156,7 +156,7 @@ async fn dispatch_command(
|
|||||||
};
|
};
|
||||||
if has_session {
|
if has_session {
|
||||||
return session_commands::handle_followup(
|
return session_commands::handle_followup(
|
||||||
db, text, channel_id, sender_id, conn_mgr, bridge, lang,
|
db, text, channel_id, sender_id, conn_mgr, bridge, lang, prefix,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -193,23 +193,23 @@ async fn dispatch_command(
|
|||||||
|
|
||||||
// Session commands
|
// Session commands
|
||||||
"folder" => {
|
"folder" => {
|
||||||
session_commands::handle_folder(db, args, channel_id, sender_id, lang).await
|
session_commands::handle_folder(db, args, channel_id, sender_id, lang, prefix).await
|
||||||
}
|
}
|
||||||
"agent" => {
|
"agent" => {
|
||||||
session_commands::handle_agent(db, args, channel_id, sender_id, lang).await
|
session_commands::handle_agent(db, args, channel_id, sender_id, lang, prefix).await
|
||||||
}
|
}
|
||||||
"task" | "do" => {
|
"task" | "do" => {
|
||||||
session_commands::handle_task(
|
session_commands::handle_task(
|
||||||
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang,
|
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang, prefix,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
"sessions" => {
|
"sessions" => {
|
||||||
session_commands::handle_sessions(db, channel_id, sender_id, lang).await
|
session_commands::handle_sessions(db, channel_id, sender_id, lang, prefix).await
|
||||||
}
|
}
|
||||||
"resume" => {
|
"resume" => {
|
||||||
session_commands::handle_resume(
|
session_commands::handle_resume(
|
||||||
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang,
|
db, args, channel_id, sender_id, conn_mgr, emitter, bridge, lang, prefix,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -610,14 +610,14 @@ pub fn help_title(lang: Lang) -> &'static str {
|
|||||||
pub fn help_body(lang: Lang, prefix: &str) -> String {
|
pub fn help_body(lang: Lang, prefix: &str) -> String {
|
||||||
match lang {
|
match lang {
|
||||||
Lang::ZhCn => format!(
|
Lang::ZhCn => format!(
|
||||||
"📂 {prefix}folder - 选择工作目录\n\
|
"{prefix}folder - 选择工作目录\n\
|
||||||
🤖 {prefix}agent - 选择 Agent\n\
|
{prefix}agent - 选择 Agent\n\
|
||||||
🚀 {prefix}task <描述> - 创建会话并执行任务\n\
|
{prefix}task <描述> - 创建会话并执行任务\n\
|
||||||
📋 {prefix}sessions - 当前目录的活跃会话\n\
|
{prefix}sessions - 当前目录的活跃会话\n\
|
||||||
▶️ {prefix}resume <ID> - 恢复已有会话\n\
|
{prefix}resume <ID> - 恢复已有会话\n\
|
||||||
⏹️ {prefix}cancel - 取消当前任务\n\
|
{prefix}cancel - 取消当前任务\n\
|
||||||
✅ {prefix}approve [always] - 批准权限请求\n\
|
{prefix}approve [always] - 批准权限请求\n\
|
||||||
❌ {prefix}deny - 拒绝权限请求\n\
|
{prefix}deny - 拒绝权限请求\n\
|
||||||
\n\
|
\n\
|
||||||
{prefix}recent - 最近 5 条会话\n\
|
{prefix}recent - 最近 5 条会话\n\
|
||||||
{prefix}search <关键词> - 搜索会话\n\
|
{prefix}search <关键词> - 搜索会话\n\
|
||||||
@@ -626,81 +626,169 @@ pub fn help_body(lang: Lang, prefix: &str) -> String {
|
|||||||
{prefix}status - 渠道连接状态\n\
|
{prefix}status - 渠道连接状态\n\
|
||||||
{prefix}help - 显示帮助\n\
|
{prefix}help - 显示帮助\n\
|
||||||
\n\
|
\n\
|
||||||
💡 有活跃会话时,直接发文本即可继续对话"
|
有活跃会话时,直接发文本即可继续对话"
|
||||||
),
|
),
|
||||||
Lang::ZhTw => format!(
|
Lang::ZhTw => format!(
|
||||||
"{prefix}recent - 最近 5 條對話\n\
|
"{prefix}folder - 選擇工作目錄\n\
|
||||||
|
{prefix}agent - 選擇 Agent\n\
|
||||||
|
{prefix}task <描述> - 建立對話並執行任務\n\
|
||||||
|
{prefix}sessions - 當前目錄的活躍對話\n\
|
||||||
|
{prefix}resume <ID> - 恢復已有對話\n\
|
||||||
|
{prefix}cancel - 取消當前任務\n\
|
||||||
|
{prefix}approve [always] - 批准權限請求\n\
|
||||||
|
{prefix}deny - 拒絕權限請求\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - 最近 5 條對話\n\
|
||||||
{prefix}search <關鍵字> - 搜尋對話\n\
|
{prefix}search <關鍵字> - 搜尋對話\n\
|
||||||
{prefix}detail <ID> - 對話詳情\n\
|
{prefix}detail <ID> - 對話詳情\n\
|
||||||
{prefix}today - 今日活動匯總\n\
|
{prefix}today - 今日活動匯總\n\
|
||||||
{prefix}status - 頻道連線狀態\n\
|
{prefix}status - 頻道連線狀態\n\
|
||||||
{prefix}help - 顯示幫助"
|
{prefix}help - 顯示幫助\n\
|
||||||
|
\n\
|
||||||
|
有活躍對話時,直接發文字即可繼續對話"
|
||||||
),
|
),
|
||||||
Lang::Ja => format!(
|
Lang::Ja => format!(
|
||||||
"{prefix}recent - 最新5件のセッション\n\
|
"{prefix}folder - 作業フォルダを選択\n\
|
||||||
|
{prefix}agent - エージェントを選択\n\
|
||||||
|
{prefix}task <説明> - セッションを作成してタスクを実行\n\
|
||||||
|
{prefix}sessions - フォルダ内のアクティブセッション\n\
|
||||||
|
{prefix}resume <ID> - セッションを再開\n\
|
||||||
|
{prefix}cancel - 現在のタスクをキャンセル\n\
|
||||||
|
{prefix}approve [always] - 権限を承認\n\
|
||||||
|
{prefix}deny - 権限を拒否\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - 最新5件のセッション\n\
|
||||||
{prefix}search <キーワード> - セッション検索\n\
|
{prefix}search <キーワード> - セッション検索\n\
|
||||||
{prefix}detail <ID> - セッション詳細\n\
|
{prefix}detail <ID> - セッション詳細\n\
|
||||||
{prefix}today - 本日の活動まとめ\n\
|
{prefix}today - 本日の活動まとめ\n\
|
||||||
{prefix}status - チャンネル接続状況\n\
|
{prefix}status - チャンネル接続状況\n\
|
||||||
{prefix}help - ヘルプを表示"
|
{prefix}help - ヘルプを表示\n\
|
||||||
|
\n\
|
||||||
|
セッションがアクティブな場合、テキストを送信するだけで会話を続けられます"
|
||||||
),
|
),
|
||||||
Lang::Ko => format!(
|
Lang::Ko => format!(
|
||||||
"{prefix}recent - 최근 5개 대화\n\
|
"{prefix}folder - 작업 폴더 선택\n\
|
||||||
|
{prefix}agent - 에이전트 선택\n\
|
||||||
|
{prefix}task <설명> - 세션 생성 및 작업 실행\n\
|
||||||
|
{prefix}sessions - 폴더 내 활성 세션\n\
|
||||||
|
{prefix}resume <ID> - 세션 재개\n\
|
||||||
|
{prefix}cancel - 현재 작업 취소\n\
|
||||||
|
{prefix}approve [always] - 권한 승인\n\
|
||||||
|
{prefix}deny - 권한 거부\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - 최근 5개 대화\n\
|
||||||
{prefix}search <키워드> - 대화 검색\n\
|
{prefix}search <키워드> - 대화 검색\n\
|
||||||
{prefix}detail <ID> - 대화 상세\n\
|
{prefix}detail <ID> - 대화 상세\n\
|
||||||
{prefix}today - 오늘의 활동 요약\n\
|
{prefix}today - 오늘의 활동 요약\n\
|
||||||
{prefix}status - 채널 연결 상태\n\
|
{prefix}status - 채널 연결 상태\n\
|
||||||
{prefix}help - 도움말 표시"
|
{prefix}help - 도움말 표시\n\
|
||||||
|
\n\
|
||||||
|
세션이 활성화된 경우 텍스트를 보내면 대화를 계속할 수 있습니다"
|
||||||
),
|
),
|
||||||
Lang::Es => format!(
|
Lang::Es => format!(
|
||||||
"{prefix}recent - 5 conversaciones más recientes\n\
|
"{prefix}folder - Seleccionar carpeta de trabajo\n\
|
||||||
|
{prefix}agent - Seleccionar agente\n\
|
||||||
|
{prefix}task <desc> - Crear sesion y ejecutar tarea\n\
|
||||||
|
{prefix}sessions - Sesiones activas en la carpeta\n\
|
||||||
|
{prefix}resume <ID> - Reanudar una sesion\n\
|
||||||
|
{prefix}cancel - Cancelar tarea actual\n\
|
||||||
|
{prefix}approve [always] - Aprobar permiso\n\
|
||||||
|
{prefix}deny - Denegar permiso\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - 5 conversaciones mas recientes\n\
|
||||||
{prefix}search <palabra> - Buscar conversaciones\n\
|
{prefix}search <palabra> - Buscar conversaciones\n\
|
||||||
{prefix}detail <ID> - Detalles de conversación\n\
|
{prefix}detail <ID> - Detalles de conversacion\n\
|
||||||
{prefix}today - Resumen de hoy\n\
|
{prefix}today - Resumen de hoy\n\
|
||||||
{prefix}status - Estado de canales\n\
|
{prefix}status - Estado de canales\n\
|
||||||
{prefix}help - Mostrar ayuda"
|
{prefix}help - Mostrar ayuda\n\
|
||||||
|
\n\
|
||||||
|
Cuando hay una sesion activa, simplemente escriba texto para continuar"
|
||||||
),
|
),
|
||||||
Lang::De => format!(
|
Lang::De => format!(
|
||||||
"{prefix}recent - 5 neueste Sitzungen\n\
|
"{prefix}folder - Arbeitsordner auswahlen\n\
|
||||||
|
{prefix}agent - Agent auswahlen\n\
|
||||||
|
{prefix}task <Beschreibung> - Sitzung erstellen und Aufgabe ausfuhren\n\
|
||||||
|
{prefix}sessions - Aktive Sitzungen im Ordner\n\
|
||||||
|
{prefix}resume <ID> - Sitzung fortsetzen\n\
|
||||||
|
{prefix}cancel - Aktuelle Aufgabe abbrechen\n\
|
||||||
|
{prefix}approve [always] - Berechtigung genehmigen\n\
|
||||||
|
{prefix}deny - Berechtigung verweigern\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - 5 neueste Sitzungen\n\
|
||||||
{prefix}search <Stichwort> - Sitzungen suchen\n\
|
{prefix}search <Stichwort> - Sitzungen suchen\n\
|
||||||
{prefix}detail <ID> - Sitzungsdetails\n\
|
{prefix}detail <ID> - Sitzungsdetails\n\
|
||||||
{prefix}today - Heutige Zusammenfassung\n\
|
{prefix}today - Heutige Zusammenfassung\n\
|
||||||
{prefix}status - Kanalstatus\n\
|
{prefix}status - Kanalstatus\n\
|
||||||
{prefix}help - Hilfe anzeigen"
|
{prefix}help - Hilfe anzeigen\n\
|
||||||
|
\n\
|
||||||
|
Bei aktiver Sitzung einfach Text eingeben, um das Gesprach fortzusetzen"
|
||||||
),
|
),
|
||||||
Lang::Fr => format!(
|
Lang::Fr => format!(
|
||||||
"{prefix}recent - 5 dernières sessions\n\
|
"{prefix}folder - Selectionner le dossier de travail\n\
|
||||||
{prefix}search <mot-clé> - Rechercher des sessions\n\
|
{prefix}agent - Selectionner l'agent\n\
|
||||||
{prefix}detail <ID> - Détails de la session\n\
|
{prefix}task <desc> - Creer une session et executer une tache\n\
|
||||||
{prefix}today - Résumé du jour\n\
|
{prefix}sessions - Sessions actives dans le dossier\n\
|
||||||
|
{prefix}resume <ID> - Reprendre une session\n\
|
||||||
|
{prefix}cancel - Annuler la tache en cours\n\
|
||||||
|
{prefix}approve [always] - Approuver la permission\n\
|
||||||
|
{prefix}deny - Refuser la permission\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - 5 dernieres sessions\n\
|
||||||
|
{prefix}search <mot-cle> - Rechercher des sessions\n\
|
||||||
|
{prefix}detail <ID> - Details de la session\n\
|
||||||
|
{prefix}today - Resume du jour\n\
|
||||||
{prefix}status - Statut des canaux\n\
|
{prefix}status - Statut des canaux\n\
|
||||||
{prefix}help - Afficher l'aide"
|
{prefix}help - Afficher l'aide\n\
|
||||||
|
\n\
|
||||||
|
Lorsqu'une session est active, envoyez du texte pour continuer la conversation"
|
||||||
),
|
),
|
||||||
Lang::Pt => format!(
|
Lang::Pt => format!(
|
||||||
"{prefix}recent - 5 sessões mais recentes\n\
|
"{prefix}folder - Selecionar pasta de trabalho\n\
|
||||||
{prefix}search <palavra> - Buscar sessões\n\
|
{prefix}agent - Selecionar agente\n\
|
||||||
{prefix}detail <ID> - Detalhes da sessão\n\
|
{prefix}task <desc> - Criar sessao e executar tarefa\n\
|
||||||
|
{prefix}sessions - Sessoes ativas na pasta\n\
|
||||||
|
{prefix}resume <ID> - Retomar uma sessao\n\
|
||||||
|
{prefix}cancel - Cancelar tarefa atual\n\
|
||||||
|
{prefix}approve [always] - Aprovar permissao\n\
|
||||||
|
{prefix}deny - Negar permissao\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - 5 sessoes mais recentes\n\
|
||||||
|
{prefix}search <palavra> - Buscar sessoes\n\
|
||||||
|
{prefix}detail <ID> - Detalhes da sessao\n\
|
||||||
{prefix}today - Resumo de hoje\n\
|
{prefix}today - Resumo de hoje\n\
|
||||||
{prefix}status - Status dos canais\n\
|
{prefix}status - Status dos canais\n\
|
||||||
{prefix}help - Mostrar ajuda"
|
{prefix}help - Mostrar ajuda\n\
|
||||||
|
\n\
|
||||||
|
Quando uma sessao esta ativa, basta digitar texto para continuar a conversa"
|
||||||
),
|
),
|
||||||
Lang::Ar => format!(
|
Lang::Ar => format!(
|
||||||
"{prefix}recent - أحدث 5 جلسات\n\
|
"{prefix}folder - اختيار مجلد العمل\n\
|
||||||
|
{prefix}agent - اختيار الوكيل\n\
|
||||||
|
{prefix}task <وصف> - انشاء جلسة وتنفيذ مهمة\n\
|
||||||
|
{prefix}sessions - الجلسات النشطة في المجلد\n\
|
||||||
|
{prefix}resume <ID> - استئناف جلسة\n\
|
||||||
|
{prefix}cancel - الغاء المهمة الحالية\n\
|
||||||
|
{prefix}approve [always] - الموافقة على الاذن\n\
|
||||||
|
{prefix}deny - رفض الاذن\n\
|
||||||
|
\n\
|
||||||
|
{prefix}recent - احدث 5 جلسات\n\
|
||||||
{prefix}search <كلمة> - البحث في الجلسات\n\
|
{prefix}search <كلمة> - البحث في الجلسات\n\
|
||||||
{prefix}detail <ID> - تفاصيل الجلسة\n\
|
{prefix}detail <ID> - تفاصيل الجلسة\n\
|
||||||
{prefix}today - ملخص اليوم\n\
|
{prefix}today - ملخص اليوم\n\
|
||||||
{prefix}status - حالة القنوات\n\
|
{prefix}status - حالة القنوات\n\
|
||||||
{prefix}help - عرض المساعدة"
|
{prefix}help - عرض المساعدة\n\
|
||||||
|
\n\
|
||||||
|
عندما تكون الجلسة نشطة، ارسل نصا لمتابعة المحادثة"
|
||||||
),
|
),
|
||||||
Lang::En => format!(
|
Lang::En => format!(
|
||||||
"📂 {prefix}folder - Select working folder\n\
|
"{prefix}folder - Select working folder\n\
|
||||||
🤖 {prefix}agent - Select agent\n\
|
{prefix}agent - Select agent\n\
|
||||||
🚀 {prefix}task <desc> - Create session & run task\n\
|
{prefix}task <desc> - Create session & run task\n\
|
||||||
📋 {prefix}sessions - Active sessions in folder\n\
|
{prefix}sessions - Active sessions in folder\n\
|
||||||
▶️ {prefix}resume <ID> - Resume a session\n\
|
{prefix}resume <ID> - Resume a session\n\
|
||||||
⏹️ {prefix}cancel - Cancel current task\n\
|
{prefix}cancel - Cancel current task\n\
|
||||||
✅ {prefix}approve [always] - Approve permission\n\
|
{prefix}approve [always] - Approve permission\n\
|
||||||
❌ {prefix}deny - Deny permission\n\
|
{prefix}deny - Deny permission\n\
|
||||||
\n\
|
\n\
|
||||||
{prefix}recent - 5 most recent conversations\n\
|
{prefix}recent - 5 most recent conversations\n\
|
||||||
{prefix}search <keyword> - Search conversations\n\
|
{prefix}search <keyword> - Search conversations\n\
|
||||||
@@ -709,7 +797,7 @@ pub fn help_body(lang: Lang, prefix: &str) -> String {
|
|||||||
{prefix}status - Channel connection status\n\
|
{prefix}status - Channel connection status\n\
|
||||||
{prefix}help - Show help\n\
|
{prefix}help - Show help\n\
|
||||||
\n\
|
\n\
|
||||||
💡 When a session is active, just type text to continue the conversation"
|
When a session is active, just type text to continue the conversation"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -810,3 +898,20 @@ pub fn unknown_command_title(lang: Lang) -> &'static str {
|
|||||||
Lang::En => "Unknown Command",
|
Lang::En => "Unknown Command",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Session progress messages ──
|
||||||
|
|
||||||
|
pub fn agent_responding(lang: Lang) -> &'static str {
|
||||||
|
match lang {
|
||||||
|
Lang::ZhCn => "Claude Code 正在响应中...",
|
||||||
|
Lang::ZhTw => "Claude Code 正在回應中...",
|
||||||
|
Lang::Ja => "Claude Code が応答中...",
|
||||||
|
Lang::Ko => "Claude Code 응답 중...",
|
||||||
|
Lang::Es => "Claude Code respondiendo...",
|
||||||
|
Lang::De => "Claude Code antwortet...",
|
||||||
|
Lang::Fr => "Claude Code en cours de reponse...",
|
||||||
|
Lang::Pt => "Claude Code respondendo...",
|
||||||
|
Lang::Ar => "...Claude Code يستجيب",
|
||||||
|
Lang::En => "Claude Code is responding...",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,14 +24,15 @@ pub async fn handle_folder(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return list_folders(db, channel_id, sender_id, lang).await;
|
return list_folders(db, channel_id, sender_id, lang, prefix).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try parse as index (1-based)
|
// Try parse as index (1-based)
|
||||||
if let Ok(idx) = args.parse::<usize>() {
|
if let Ok(idx) = args.parse::<usize>() {
|
||||||
return select_folder_by_index(db, idx, channel_id, sender_id, lang).await;
|
return select_folder_by_index(db, idx, channel_id, sender_id, lang, prefix).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat as path
|
// Treat as path
|
||||||
@@ -43,6 +44,7 @@ async fn list_folders(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
let folders = match folder_service::list_folders(db).await {
|
let folders = match folder_service::list_folders(db).await {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
@@ -77,10 +79,11 @@ async fn list_folders(
|
|||||||
|
|
||||||
body.push_str(&format!(
|
body.push_str(&format!(
|
||||||
"\n{}",
|
"\n{}",
|
||||||
t(
|
tp(
|
||||||
lang,
|
lang,
|
||||||
"Reply /folder <number> to select.",
|
prefix,
|
||||||
"回复 /folder <数字> 选择目录。"
|
"Reply {prefix}folder <number> to select.",
|
||||||
|
"回复 {prefix}folder <数字> 选择目录。"
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -94,6 +97,7 @@ async fn select_folder_by_index(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
return RichMessage::info(t(lang, "Index starts from 1.", "序号从 1 开始。"));
|
return RichMessage::info(t(lang, "Index starts from 1.", "序号从 1 开始。"));
|
||||||
@@ -105,10 +109,11 @@ async fn select_folder_by_index(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some(folder) = folders.get(idx - 1) else {
|
let Some(folder) = folders.get(idx - 1) else {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"Index out of range. Use /folder to list.",
|
prefix,
|
||||||
"序号超出范围,请使用 /folder 查看列表。",
|
"Index out of range. Use {prefix}folder to list.",
|
||||||
|
"序号超出范围,请使用 {prefix}folder 查看列表。",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -146,14 +151,15 @@ pub async fn handle_agent(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return list_agents(db, channel_id, sender_id, lang).await;
|
return list_agents(db, channel_id, sender_id, lang, prefix).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try parse as index
|
// Try parse as index
|
||||||
if let Ok(idx) = args.parse::<usize>() {
|
if let Ok(idx) = args.parse::<usize>() {
|
||||||
return select_agent_by_index(db, idx, channel_id, sender_id, lang).await;
|
return select_agent_by_index(db, idx, channel_id, sender_id, lang, prefix).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try parse as agent type name
|
// Try parse as agent type name
|
||||||
@@ -165,6 +171,7 @@ async fn list_agents(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
let agents = all_acp_agents();
|
let agents = all_acp_agents();
|
||||||
let ctx = sender_context_service::get_or_create(db, channel_id, sender_id)
|
let ctx = sender_context_service::get_or_create(db, channel_id, sender_id)
|
||||||
@@ -185,10 +192,11 @@ async fn list_agents(
|
|||||||
|
|
||||||
body.push_str(&format!(
|
body.push_str(&format!(
|
||||||
"\n{}",
|
"\n{}",
|
||||||
t(
|
tp(
|
||||||
lang,
|
lang,
|
||||||
"Reply /agent <number> or /agent <name> to select.",
|
prefix,
|
||||||
"回复 /agent <数字> 或 /agent <名称> 选择。"
|
"Reply {prefix}agent <number> or {prefix}agent <name> to select.",
|
||||||
|
"回复 {prefix}agent <数字> 或 {prefix}agent <名称> 选择。"
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -202,13 +210,15 @@ async fn select_agent_by_index(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
let agents = all_acp_agents();
|
let agents = all_acp_agents();
|
||||||
if idx == 0 || idx > agents.len() {
|
if idx == 0 || idx > agents.len() {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"Index out of range. Use /agent to list.",
|
prefix,
|
||||||
"序号超出范围,请使用 /agent 查看列表。",
|
"Index out of range. Use {prefix}agent to list.",
|
||||||
|
"序号超出范围,请使用 {prefix}agent 查看列表。",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,12 +267,14 @@ pub async fn handle_task(
|
|||||||
emitter: &EventEmitter,
|
emitter: &EventEmitter,
|
||||||
bridge: &Arc<Mutex<SessionBridge>>,
|
bridge: &Arc<Mutex<SessionBridge>>,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
if task_description.is_empty() {
|
if task_description.is_empty() {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"Usage: /task <description>",
|
prefix,
|
||||||
"用法: /task <任务描述>",
|
"Usage: {prefix}task <description>",
|
||||||
|
"用法: {prefix}task <任务描述>",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,10 +287,11 @@ pub async fn handle_task(
|
|||||||
let folder_id = match ctx.current_folder_id {
|
let folder_id = match ctx.current_folder_id {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
None => {
|
None => {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"No folder selected. Use /folder first.",
|
prefix,
|
||||||
"未选择工作目录,请先使用 /folder 选择。",
|
"No folder selected. Use {prefix}folder first.",
|
||||||
|
"未选择工作目录,请先使用 {prefix}folder 选择。",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -287,10 +300,11 @@ pub async fn handle_task(
|
|||||||
let folder = match folder_service::get_folder_by_id(db, folder_id).await {
|
let folder = match folder_service::get_folder_by_id(db, folder_id).await {
|
||||||
Ok(Some(f)) => f,
|
Ok(Some(f)) => f,
|
||||||
_ => {
|
_ => {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"Folder not found. Use /folder to select.",
|
prefix,
|
||||||
"目录不存在,请使用 /folder 重新选择。",
|
"Folder not found. Use {prefix}folder to select.",
|
||||||
|
"目录不存在,请使用 {prefix}folder 重新选择。",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -381,6 +395,7 @@ pub async fn handle_sessions(
|
|||||||
channel_id: i32,
|
channel_id: i32,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
let ctx = match sender_context_service::get_or_create(db, channel_id, sender_id).await {
|
let ctx = match sender_context_service::get_or_create(db, channel_id, sender_id).await {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
@@ -390,10 +405,11 @@ pub async fn handle_sessions(
|
|||||||
let folder_id = match ctx.current_folder_id {
|
let folder_id = match ctx.current_folder_id {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
None => {
|
None => {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"No folder selected. Use /folder first.",
|
prefix,
|
||||||
"未选择工作目录,请先使用 /folder 选择。",
|
"No folder selected. Use {prefix}folder first.",
|
||||||
|
"未选择工作目录,请先使用 {prefix}folder 选择。",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -456,10 +472,11 @@ pub async fn handle_sessions(
|
|||||||
|
|
||||||
body.push_str(&format!(
|
body.push_str(&format!(
|
||||||
"\n{}",
|
"\n{}",
|
||||||
t(
|
tp(
|
||||||
lang,
|
lang,
|
||||||
"Reply /resume <id> to continue.",
|
prefix,
|
||||||
"回复 /resume <ID> 继续会话。"
|
"Reply {prefix}resume <id> to continue.",
|
||||||
|
"回复 {prefix}resume <ID> 继续会话。"
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -482,14 +499,16 @@ pub async fn handle_resume(
|
|||||||
emitter: &EventEmitter,
|
emitter: &EventEmitter,
|
||||||
bridge: &Arc<Mutex<SessionBridge>>,
|
bridge: &Arc<Mutex<SessionBridge>>,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
let conversation_id: i32 = match args.parse() {
|
let conversation_id: i32 = match args.parse() {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"Usage: /resume <conversation_id>",
|
prefix,
|
||||||
"用法: /resume <会话ID>",
|
"Usage: {prefix}resume <conversation_id>",
|
||||||
|
"用法: {prefix}resume <会话ID>",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -753,6 +772,7 @@ pub async fn handle_followup(
|
|||||||
conn_mgr: &ConnectionManager,
|
conn_mgr: &ConnectionManager,
|
||||||
bridge: &Arc<Mutex<SessionBridge>>,
|
bridge: &Arc<Mutex<SessionBridge>>,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
|
prefix: &str,
|
||||||
) -> RichMessage {
|
) -> RichMessage {
|
||||||
let ctx = match sender_context_service::get_or_create(db, channel_id, sender_id).await {
|
let ctx = match sender_context_service::get_or_create(db, channel_id, sender_id).await {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
@@ -762,10 +782,11 @@ pub async fn handle_followup(
|
|||||||
let connection_id = match &ctx.current_connection_id {
|
let connection_id = match &ctx.current_connection_id {
|
||||||
Some(id) => id.clone(),
|
Some(id) => id.clone(),
|
||||||
None => {
|
None => {
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"No active session. Use /task to start one.",
|
prefix,
|
||||||
"没有活跃的会话,请使用 /task 开始新任务。",
|
"No active session. Use {prefix}task to start one.",
|
||||||
|
"没有活跃的会话,请使用 {prefix}task 开始新任务。",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -777,10 +798,11 @@ pub async fn handle_followup(
|
|||||||
// Connection lost, clear context
|
// Connection lost, clear context
|
||||||
drop(bridge_guard);
|
drop(bridge_guard);
|
||||||
let _ = sender_context_service::clear_session(db, channel_id, sender_id).await;
|
let _ = sender_context_service::clear_session(db, channel_id, sender_id).await;
|
||||||
return RichMessage::info(t(
|
return RichMessage::info(tp(
|
||||||
lang,
|
lang,
|
||||||
"Session connection lost. Use /task to start a new one.",
|
prefix,
|
||||||
"会话连接已断开,请使用 /task 开始新任务。",
|
"Session connection lost. Use {prefix}task to start a new one.",
|
||||||
|
"会话连接已断开,请使用 {prefix}task 开始新任务。",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -812,6 +834,11 @@ fn t(lang: Lang, en: &str, zh: &str) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like `t()` but replaces `{prefix}` placeholders with the actual command prefix.
|
||||||
|
fn tp(lang: Lang, prefix: &str, en: &str, zh: &str) -> String {
|
||||||
|
t(lang, en, zh).replace("{prefix}", prefix)
|
||||||
|
}
|
||||||
|
|
||||||
fn agent_type_to_string(at: AgentType) -> String {
|
fn agent_type_to_string(at: AgentType) -> String {
|
||||||
serde_json::to_value(at)
|
serde_json::to_value(at)
|
||||||
.ok()
|
.ok()
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ use crate::web::event_bridge::WebEventBroadcaster;
|
|||||||
|
|
||||||
use super::manager::ChatChannelManager;
|
use super::manager::ChatChannelManager;
|
||||||
|
|
||||||
const FLUSH_INTERVAL_SECS: u64 = 5;
|
const FLUSH_INTERVAL_SECS: u64 = 10;
|
||||||
const BUFFER_FLUSH_THRESHOLD: usize = 500;
|
const BUFFER_FLUSH_THRESHOLD: usize = 500;
|
||||||
const MAX_MESSAGE_LEN: usize = 2000;
|
const MAX_MESSAGE_LEN: usize = 2000;
|
||||||
const MESSAGE_LANGUAGE_KEY: &str = "chat_message_language";
|
const MESSAGE_LANGUAGE_KEY: &str = "chat_message_language";
|
||||||
|
const COMMAND_PREFIX_KEY: &str = "chat_command_prefix";
|
||||||
|
const DEFAULT_COMMAND_PREFIX: &str = "/";
|
||||||
|
|
||||||
pub fn spawn_session_event_subscriber(
|
pub fn spawn_session_event_subscriber(
|
||||||
broadcaster: Arc<WebEventBroadcaster>,
|
broadcaster: Arc<WebEventBroadcaster>,
|
||||||
@@ -59,7 +61,7 @@ pub fn spawn_session_event_subscriber(
|
|||||||
}
|
}
|
||||||
_ = tokio::time::sleep(Duration::from_secs(FLUSH_INTERVAL_SECS)) => {
|
_ = tokio::time::sleep(Duration::from_secs(FLUSH_INTERVAL_SECS)) => {
|
||||||
if last_heartbeat.elapsed() >= Duration::from_secs(FLUSH_INTERVAL_SECS) {
|
if last_heartbeat.elapsed() >= Duration::from_secs(FLUSH_INTERVAL_SECS) {
|
||||||
flush_progress(&bridge, &manager).await;
|
flush_progress(&bridge, &manager, &db_conn).await;
|
||||||
last_heartbeat = Instant::now();
|
last_heartbeat = Instant::now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,6 +79,14 @@ async fn get_lang(db: &DatabaseConnection) -> Lang {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_prefix(db: &DatabaseConnection) -> String {
|
||||||
|
app_metadata_service::get_value(db, COMMAND_PREFIX_KEY)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| DEFAULT_COMMAND_PREFIX.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_acp_event_payload(
|
async fn handle_acp_event_payload(
|
||||||
payload: &serde_json::Value,
|
payload: &serde_json::Value,
|
||||||
bridge: &Arc<Mutex<SessionBridge>>,
|
bridge: &Arc<Mutex<SessionBridge>>,
|
||||||
@@ -135,11 +145,11 @@ async fn handle_acp_event_payload(
|
|||||||
&& session.last_flushed.elapsed() >= Duration::from_secs(2)
|
&& session.last_flushed.elapsed() >= Duration::from_secs(2)
|
||||||
{
|
{
|
||||||
let channel_id = session.channel_id;
|
let channel_id = session.channel_id;
|
||||||
let buf_len = session.content_buffer.len();
|
|
||||||
let last_tool = session.tool_calls.last().cloned();
|
let last_tool = session.tool_calls.last().cloned();
|
||||||
session.last_flushed = Instant::now();
|
session.last_flushed = Instant::now();
|
||||||
|
|
||||||
let mut status = format!("... ({buf_len} chars)");
|
let lang = get_lang(db).await;
|
||||||
|
let mut status = super::i18n::agent_responding(lang).to_string();
|
||||||
if let Some(tool) = last_tool {
|
if let Some(tool) = last_tool {
|
||||||
status.push_str(&format!(" | {tool}"));
|
status.push_str(&format!(" | {tool}"));
|
||||||
}
|
}
|
||||||
@@ -252,12 +262,13 @@ async fn handle_acp_event_payload(
|
|||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
let lang = get_lang(db).await;
|
let lang = get_lang(db).await;
|
||||||
|
let prefix = get_prefix(db).await;
|
||||||
let body = match lang {
|
let body = match lang {
|
||||||
Lang::ZhCn | Lang::ZhTw => {
|
Lang::ZhCn | Lang::ZhTw => {
|
||||||
format!("Agent 请求权限: {tool_desc}\n\n/approve 批准 | /deny 拒绝 | /approve always 自动批准")
|
format!("Agent 请求权限: {tool_desc}\n\n{prefix}approve 批准 | {prefix}deny 拒绝 | {prefix}approve always 自动批准")
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
format!("Agent requests permission: {tool_desc}\n\n/approve | /deny | /approve always")
|
format!("Agent requests permission: {tool_desc}\n\n{prefix}approve | {prefix}deny | {prefix}approve always")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -387,7 +398,11 @@ async fn handle_acp_event_payload(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn flush_progress(bridge: &Arc<Mutex<SessionBridge>>, manager: &ChatChannelManager) {
|
async fn flush_progress(
|
||||||
|
bridge: &Arc<Mutex<SessionBridge>>,
|
||||||
|
manager: &ChatChannelManager,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) {
|
||||||
let updates: Vec<(i32, String)> = {
|
let updates: Vec<(i32, String)> = {
|
||||||
let mut guard = bridge.lock().await;
|
let mut guard = bridge.lock().await;
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
@@ -395,13 +410,14 @@ async fn flush_progress(bridge: &Arc<Mutex<SessionBridge>>, manager: &ChatChanne
|
|||||||
if !session.content_buffer.is_empty()
|
if !session.content_buffer.is_empty()
|
||||||
&& session.last_flushed.elapsed() >= Duration::from_secs(FLUSH_INTERVAL_SECS)
|
&& session.last_flushed.elapsed() >= Duration::from_secs(FLUSH_INTERVAL_SECS)
|
||||||
{
|
{
|
||||||
let buf_len = session.content_buffer.len();
|
|
||||||
let tool_count = session.tool_calls.len();
|
|
||||||
session.last_flushed = Instant::now();
|
session.last_flushed = Instant::now();
|
||||||
out.push((
|
let last_tool = session.tool_calls.last().cloned();
|
||||||
session.channel_id,
|
let lang = get_lang(db).await;
|
||||||
format!("... ({buf_len} chars, {tool_count} tools)"),
|
let mut status = super::i18n::agent_responding(lang).to_string();
|
||||||
));
|
if let Some(tool) = last_tool {
|
||||||
|
status.push_str(&format!(" | {tool}"));
|
||||||
|
}
|
||||||
|
out.push((session.channel_id, status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
|
|||||||
Reference in New Issue
Block a user