,
+ ): EnhancedExternalBot[] {
+ return [...bots.entries()].reduce((bots, [id, perm]) => {
+ const bot = allBots.get(id);
+ if (bot !== undefined) {
+ bots.push({
+ ...bot,
+ grantedPermissions: perm,
+ });
+ }
+ return bots;
+ }, [] as EnhancedExternalBot[]);
+ }
function matchingUsers(
term: string,
@@ -130,8 +160,16 @@
dispatch("showInviteUsers");
}
- function matchesSearch(searchTermLower: string, user: UserSummary): boolean {
+ function matchesSearch(searchTermLower: string, user: UserSummary | ExternalBot): boolean {
if (searchTermLower === "") return true;
+ if (user.kind === "external_bot") {
+ return (
+ user.name.toLowerCase().includes(searchTermLower) ||
+ (user.description !== undefined &&
+ user.description.toLocaleLowerCase().includes(searchTermLower))
+ );
+ }
+
if (user.username === undefined) return true;
return (
user.username.toLowerCase().includes(searchTermLower) ||
@@ -196,7 +234,7 @@
on:showInviteUsers={showInviteUsers} />
{/if}
-{#if collection.level === "community"}
+{#if collection.kind !== "channel"}
-
+ {#if collection.kind === "community"}
+
+ {/if}
{#if botsEnabled}
-{:else if selectedTab === "add-bots" && collection.kind === "community" && $selectedCommunity}
+{:else if selectedTab === "add-bots" && collection.kind !== "channel"}
-
+
{/if}
@@ -415,4 +476,11 @@
}
}
}
+
+ .member_type_label {
+ padding: $sp2 $sp4;
+ text-transform: uppercase;
+ @include font(light, normal, fs-50);
+ border-bottom: 1px solid var(--bd);
+ }
diff --git a/frontend/app/src/i18n/ar.json b/frontend/app/src/i18n/ar.json
index 55739d5282..ef179f417a 100644
--- a/frontend/app/src/i18n/ar.json
+++ b/frontend/app/src/i18n/ar.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "انشر رابطًا لشرح العضوية الماسية"
},
+ "edit": {
+ "failure": "نأسف لعدم تمكننا من تحديث أذونات الروبوت",
+ "title": "تحديث أذونات الروبوت",
+ "updateBot": "تحديث البوت"
+ },
+ "events": {
+ "add": "تم إضافة الروبوت **{botname}** بواسطة {username}",
+ "remove": "تم إزالة الروبوت **{botname}** بواسطة {username}",
+ "update": "تم تحديث الروبوت **{botname}** بواسطة {username}"
+ },
"explorer": {
"addBots": "إضافة الروبوتات",
"loadMore": "تحميل المزيد..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "إزالة البوت",
+ "removeFailed": "نأسف لأننا لم نتمكن من إزالة الروبوت",
+ "review": "مراجعة أذونات الروبوت",
+ "view": "عرض تفاصيل البوت"
+ },
"matchingCommands": "أوامر المطابقة",
+ "member": {
+ "bots": "الروبوتات",
+ "people": "الناس"
+ },
"poll": {
"desc": "إنشاء استطلاع رأي في الدردشة الحالية"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "ابدأ اقتراحًا لتحديث روبوت OpenChat الحالي"
},
+ "view": {
+ "close": "يغلق",
+ "title": "تفاصيل البوت"
+ },
"witch": {
"desc": "استدعاء الساحرة"
}
diff --git a/frontend/app/src/i18n/cn.json b/frontend/app/src/i18n/cn.json
index a2a33fe6d2..9fbf266295 100644
--- a/frontend/app/src/i18n/cn.json
+++ b/frontend/app/src/i18n/cn.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "发布链接解释钻石会员资格"
},
+ "edit": {
+ "failure": "抱歉,我们无法更新机器人权限",
+ "title": "更新机器人权限",
+ "updateBot": "更新机器人"
+ },
+ "events": {
+ "add": "机器人 **{botname}** 由 {username} 添加",
+ "remove": "机器人 **{botname}** 已被 {username} 移除",
+ "update": "机器人 **{botname}** 由 {username} 更新"
+ },
"explorer": {
"addBots": "添加机器人",
"loadMore": "加载更多..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "移除机器人",
+ "removeFailed": "抱歉,我们无法移除该机器人",
+ "review": "审查机器人权限",
+ "view": "查看机器人详细信息"
+ },
"matchingCommands": "匹配命令",
+ "member": {
+ "bots": "机器人",
+ "people": "人们"
+ },
"poll": {
"desc": "在当前聊天中创建投票"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "发起更新现有 OpenChat 机器人的提案"
},
+ "view": {
+ "close": "关闭",
+ "title": "机器人详细信息"
+ },
"witch": {
"desc": "召唤女巫"
}
diff --git a/frontend/app/src/i18n/de.json b/frontend/app/src/i18n/de.json
index 808cbe9f32..d05757820c 100644
--- a/frontend/app/src/i18n/de.json
+++ b/frontend/app/src/i18n/de.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Posten Sie einen Link, um die Diamond-Mitgliedschaft zu erklären"
},
+ "edit": {
+ "failure": "Leider konnten wir die Bot-Berechtigungen nicht aktualisieren",
+ "title": "Bot-Berechtigungen aktualisieren",
+ "updateBot": "Bot aktualisieren"
+ },
+ "events": {
+ "add": "Bot **{botname}** hinzugefügt von {username}",
+ "remove": "Bot **{botname}** von {username} entfernt",
+ "update": "Bot **{botname}** aktualisiert von {username}"
+ },
"explorer": {
"addBots": "Bots hinzufügen",
"loadMore": "Mehr laden..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Bot entfernen",
+ "removeFailed": "Es ist uns leider nicht gelungen, den Bot zu entfernen.",
+ "review": "Überprüfen Sie die Bot-Berechtigungen",
+ "view": "Botdetails anzeigen"
+ },
"matchingCommands": "Passende Befehle",
+ "member": {
+ "bots": "Bots",
+ "people": "Menschen"
+ },
"poll": {
"desc": "Erstellen Sie eine Umfrage im aktuellen Chat"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Starten Sie einen Vorschlag zur Aktualisierung eines vorhandenen OpenChat-Bots"
},
+ "view": {
+ "close": "Schließen",
+ "title": "Bot-Details"
+ },
"witch": {
"desc": "Beschwöre die Hexe"
}
diff --git a/frontend/app/src/i18n/en.json b/frontend/app/src/i18n/en.json
index 4180f9bb98..c2689844f9 100644
--- a/frontend/app/src/i18n/en.json
+++ b/frontend/app/src/i18n/en.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Post a link to explain Diamond membership"
},
+ "edit": {
+ "failure": "Sorry we were unable to update the bot permissions",
+ "title": "Update bot permissions",
+ "updateBot": "Update bot"
+ },
+ "events": {
+ "add": "Bot **{botname}** added by {username}",
+ "remove": "Bot **{botname}** removed by {username}",
+ "update": "Bot **{botname}** updated by {username}"
+ },
"explorer": {
"addBots": "Add bots",
"loadMore": "Load more..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Remove bot",
+ "removeFailed": "Sorry we were unable to remove the bot",
+ "review": "Review bot permissions",
+ "view": "View bot details"
+ },
"matchingCommands": "Matching commands",
+ "member": {
+ "bots": "Bots",
+ "people": "People"
+ },
"poll": {
"desc": "Create a poll in the current chat"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Start a proposal to update an existing OpenChat bot"
},
+ "view": {
+ "close": "Close",
+ "title": "Bot details"
+ },
"witch": {
"desc": "Summon the witch"
}
diff --git a/frontend/app/src/i18n/es.json b/frontend/app/src/i18n/es.json
index fc390a743c..d76cb81104 100644
--- a/frontend/app/src/i18n/es.json
+++ b/frontend/app/src/i18n/es.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Publica un enlace para explicar la membresía Diamante"
},
+ "edit": {
+ "failure": "Lo sentimos, no pudimos actualizar los permisos del bot.",
+ "title": "Actualizar permisos de bot",
+ "updateBot": "Actualizar bot"
+ },
+ "events": {
+ "add": "Bot **{botname}** añadido por {username}",
+ "remove": "Bot **{botname}** eliminado por {username}",
+ "update": "Bot **{botname}** actualizado por {username}"
+ },
"explorer": {
"addBots": "Agregar bots",
"loadMore": "Cargar más..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Eliminar bot",
+ "removeFailed": "Lo sentimos, no pudimos eliminar el bot.",
+ "review": "Revisar los permisos del bot",
+ "view": "Ver detalles del bot"
+ },
"matchingCommands": "Comandos coincidentes",
+ "member": {
+ "bots": "Bots",
+ "people": "Gente"
+ },
"poll": {
"desc": "Crear una encuesta en el chat actual"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Iniciar una propuesta para actualizar un bot de OpenChat existente"
},
+ "view": {
+ "close": "Cerca",
+ "title": "Detalles del bot"
+ },
"witch": {
"desc": "Invocar a la bruja"
}
diff --git a/frontend/app/src/i18n/fa.json b/frontend/app/src/i18n/fa.json
index 5c622e2af8..44719ef1fa 100644
--- a/frontend/app/src/i18n/fa.json
+++ b/frontend/app/src/i18n/fa.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "یک لینک برای توضیح عضویت در Diamond ارسال کنید"
},
+ "edit": {
+ "failure": "متأسفیم که نتوانستیم مجوزهای ربات را به روز کنیم",
+ "title": "به روز رسانی مجوزهای ربات",
+ "updateBot": "به روز رسانی ربات"
+ },
+ "events": {
+ "add": "ربات **{botname}** اضافه شده توسط {username}",
+ "remove": "ربات **{botname}** توسط {username} حذف شد",
+ "update": "ربات **{botname}** به روز شده توسط {username}"
+ },
"explorer": {
"addBots": "ربات ها را اضافه کنید",
"loadMore": "بارگیری بیشتر..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "حذف ربات",
+ "removeFailed": "متأسفیم که نتوانستیم ربات را حذف کنیم",
+ "review": "مجوزهای ربات را بررسی کنید",
+ "view": "مشاهده جزئیات ربات"
+ },
"matchingCommands": "تطبیق دستورات",
+ "member": {
+ "bots": "ربات ها",
+ "people": "مردم"
+ },
"poll": {
"desc": "یک نظرسنجی در چت فعلی ایجاد کنید"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "پیشنهادی را برای به روز رسانی ربات OpenChat موجود شروع کنید"
},
+ "view": {
+ "close": "بستن",
+ "title": "جزئیات ربات"
+ },
"witch": {
"desc": "جادوگر را احضار کن"
}
diff --git a/frontend/app/src/i18n/fr.json b/frontend/app/src/i18n/fr.json
index 4f2813f881..d42dce1e9c 100644
--- a/frontend/app/src/i18n/fr.json
+++ b/frontend/app/src/i18n/fr.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Postez un lien pour expliquer l'adhésion Diamond"
},
+ "edit": {
+ "failure": "Désolé, nous n'avons pas pu mettre à jour les autorisations du bot",
+ "title": "Mettre à jour les autorisations du bot",
+ "updateBot": "Bot de mise à jour"
+ },
+ "events": {
+ "add": "Bot **{botname}** ajouté par {username}",
+ "remove": "Bot **{botname}** supprimé par {username}",
+ "update": "Bot **{botname}** mis à jour par {username}"
+ },
"explorer": {
"addBots": "Ajouter des robots",
"loadMore": "Charger plus..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Supprimer le bot",
+ "removeFailed": "Désolé, nous n'avons pas pu supprimer le bot",
+ "review": "Examiner les autorisations du robot",
+ "view": "Afficher les détails du bot"
+ },
"matchingCommands": "Commandes correspondantes",
+ "member": {
+ "bots": "Les robots",
+ "people": "Personnes"
+ },
"poll": {
"desc": "Créer un sondage dans le chat actuel"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Démarrer une proposition pour mettre à jour un bot OpenChat existant"
},
+ "view": {
+ "close": "Fermer",
+ "title": "Détails du bot"
+ },
"witch": {
"desc": "Invoquer la sorcière"
}
diff --git a/frontend/app/src/i18n/hi.json b/frontend/app/src/i18n/hi.json
index a181181be0..319c6fb3e2 100644
--- a/frontend/app/src/i18n/hi.json
+++ b/frontend/app/src/i18n/hi.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "डायमंड सदस्यता के बारे में बताने के लिए लिंक पोस्ट करें"
},
+ "edit": {
+ "failure": "क्षमा करें, हम बॉट अनुमतियों को अपडेट करने में असमर्थ थे",
+ "title": "बॉट अनुमतियाँ अपडेट करें",
+ "updateBot": "बॉट अपडेट करें"
+ },
+ "events": {
+ "add": "बॉट **{botname}** को {username} द्वारा जोड़ा गया",
+ "remove": "बॉट **{botname}** को {username} द्वारा हटा दिया गया",
+ "update": "बॉट **{botname}** को {username} द्वारा अपडेट किया गया"
+ },
"explorer": {
"addBots": "बॉट जोड़ें",
"loadMore": "और लोड करें..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "बॉट हटाएँ",
+ "removeFailed": "खेद है कि हम बॉट को हटाने में असमर्थ रहे",
+ "review": "बॉट अनुमतियों की समीक्षा करें",
+ "view": "बॉट विवरण देखें"
+ },
"matchingCommands": "मिलान आदेश",
+ "member": {
+ "bots": "बॉट",
+ "people": "लोग"
+ },
"poll": {
"desc": "वर्तमान चैट में पोल बनाएँ"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "मौजूदा OpenChat बॉट को अपडेट करने के लिए प्रस्ताव शुरू करें"
},
+ "view": {
+ "close": "बंद करना",
+ "title": "बॉट विवरण"
+ },
"witch": {
"desc": "चुड़ैल को बुलाओ"
}
diff --git a/frontend/app/src/i18n/it.json b/frontend/app/src/i18n/it.json
index db2055cac4..00e6187a40 100644
--- a/frontend/app/src/i18n/it.json
+++ b/frontend/app/src/i18n/it.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Pubblica un link per spiegare l'appartenenza a Diamond"
},
+ "edit": {
+ "failure": "Spiacenti, non siamo riusciti ad aggiornare le autorizzazioni del bot",
+ "title": "Aggiorna i permessi del bot",
+ "updateBot": "Aggiorna bot"
+ },
+ "events": {
+ "add": "Bot **{botname}** aggiunto da {username}",
+ "remove": "Bot **{botname}** rimosso da {username}",
+ "update": "Bot **{botname}** aggiornato da {username}"
+ },
"explorer": {
"addBots": "Aggiungi bot",
"loadMore": "Carica altro..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Rimuovi bot",
+ "removeFailed": "Spiacenti, non siamo riusciti a rimuovere il bot",
+ "review": "Rivedi le autorizzazioni del bot",
+ "view": "Visualizza i dettagli del bot"
+ },
"matchingCommands": "Comandi corrispondenti",
+ "member": {
+ "bots": "I bot",
+ "people": "Persone"
+ },
"poll": {
"desc": "Crea un sondaggio nella chat corrente"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Avvia una proposta per aggiornare un bot OpenChat esistente"
},
+ "view": {
+ "close": "Vicino",
+ "title": "Dettagli del bot"
+ },
"witch": {
"desc": "Evoca la strega"
}
diff --git a/frontend/app/src/i18n/iw.json b/frontend/app/src/i18n/iw.json
index 8a3e5da289..1c1a8f322e 100644
--- a/frontend/app/src/i18n/iw.json
+++ b/frontend/app/src/i18n/iw.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "פרסם קישור להסבר על חברות Diamond"
},
+ "edit": {
+ "failure": "מצטערים שלא הצלחנו לעדכן את הרשאות הבוט",
+ "title": "עדכן הרשאות בוט",
+ "updateBot": "עדכן את הבוט"
+ },
+ "events": {
+ "add": "בוט **{botname}** נוסף על ידי {username}",
+ "remove": "הבוט **{botname}** הוסר על ידי {username}",
+ "update": "בוט **{botname}** עודכן על ידי {username}"
+ },
"explorer": {
"addBots": "הוסף בוטים",
"loadMore": "טען עוד..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "הסר בוט",
+ "removeFailed": "מצטערים שלא הצלחנו להסיר את הבוט",
+ "review": "בדוק את הרשאות הבוט",
+ "view": "הצג את פרטי הבוט"
+ },
"matchingCommands": "התאמת פקודות",
+ "member": {
+ "bots": "בוטים",
+ "people": "אֲנָשִׁים"
+ },
"poll": {
"desc": "צור סקר בצ'אט הנוכחי"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "התחל הצעה לעדכון בוט OpenChat קיים"
},
+ "view": {
+ "close": "לִסְגוֹר",
+ "title": "פרטי בוט"
+ },
"witch": {
"desc": "לזמן את המכשפה"
}
diff --git a/frontend/app/src/i18n/jp.json b/frontend/app/src/i18n/jp.json
index 6de48bb6dd..1d6ed661be 100644
--- a/frontend/app/src/i18n/jp.json
+++ b/frontend/app/src/i18n/jp.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "ダイヤモンド会員資格を説明するリンクを投稿する"
},
+ "edit": {
+ "failure": "申し訳ありませんが、ボットの権限を更新できませんでした",
+ "title": "ボットの権限を更新する",
+ "updateBot": "ボットの更新"
+ },
+ "events": {
+ "add": "ボット **{botname}** が {username} によって追加されました",
+ "remove": "ボット **{botname}** は {username} によって削除されました",
+ "update": "ボット **{botname}** が {username} によって更新されました"
+ },
"explorer": {
"addBots": "ボットを追加",
"loadMore": "さらに読み込む..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "ボットを削除",
+ "removeFailed": "申し訳ありませんが、ボットを削除できませんでした",
+ "review": "ボットの権限を確認する",
+ "view": "ボットの詳細を表示"
+ },
"matchingCommands": "一致するコマンド",
+ "member": {
+ "bots": "ボット",
+ "people": "人々"
+ },
"poll": {
"desc": "現在のチャットでアンケートを作成する"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "既存のOpenChatボットを更新する提案を開始する"
},
+ "view": {
+ "close": "近い",
+ "title": "ボットの詳細"
+ },
"witch": {
"desc": "魔女を召喚する"
}
diff --git a/frontend/app/src/i18n/pl.json b/frontend/app/src/i18n/pl.json
index 379f99564e..9cd25a193c 100644
--- a/frontend/app/src/i18n/pl.json
+++ b/frontend/app/src/i18n/pl.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Opublikuj link wyjaśniający członkostwo Diamond"
},
+ "edit": {
+ "failure": "Przepraszamy, nie udało nam się zaktualizować uprawnień bota",
+ "title": "Zaktualizuj uprawnienia bota",
+ "updateBot": "Aktualizacja bota"
+ },
+ "events": {
+ "add": "Bot **{botname}** dodany przez {username}",
+ "remove": "Bot **{botname}** został usunięty przez {username}",
+ "update": "Bot **{botname}** zaktualizowany przez {username}"
+ },
"explorer": {
"addBots": "Dodaj boty",
"loadMore": "Załaduj więcej..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Usuń bota",
+ "removeFailed": "Przepraszamy, nie udało nam się usunąć bota",
+ "review": "Przejrzyj uprawnienia bota",
+ "view": "Wyświetl szczegóły bota"
+ },
"matchingCommands": "Dopasowywanie poleceń",
+ "member": {
+ "bots": "Boty",
+ "people": "Ludzie"
+ },
"poll": {
"desc": "Utwórz ankietę w bieżącym czacie"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Rozpocznij propozycję aktualizacji istniejącego bota OpenChat"
},
+ "view": {
+ "close": "Zamknąć",
+ "title": "Szczegóły bota"
+ },
"witch": {
"desc": "Przywołaj czarownicę"
}
diff --git a/frontend/app/src/i18n/ru.json b/frontend/app/src/i18n/ru.json
index 91659d32d9..709488f85e 100644
--- a/frontend/app/src/i18n/ru.json
+++ b/frontend/app/src/i18n/ru.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Опубликуйте ссылку, чтобы объяснить суть членства Diamond"
},
+ "edit": {
+ "failure": "Извините, нам не удалось обновить разрешения бота.",
+ "title": "Обновить разрешения бота",
+ "updateBot": "Обновление бота"
+ },
+ "events": {
+ "add": "Бот **{botname}** добавлен {username}",
+ "remove": "Бот **{botname}** удален пользователем {username}",
+ "update": "Бот **{botname}** обновлен {username}"
+ },
"explorer": {
"addBots": "Добавить ботов",
"loadMore": "Загрузить еще..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Удалить бота",
+ "removeFailed": "Извините, мы не смогли удалить бота.",
+ "review": "Просмотр разрешений бота",
+ "view": "Посмотреть подробности бота"
+ },
"matchingCommands": "Соответствующие команды",
+ "member": {
+ "bots": "Боты",
+ "people": "Люди"
+ },
"poll": {
"desc": "Создать опрос в текущем чате"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Начать предложение по обновлению существующего бота OpenChat"
},
+ "view": {
+ "close": "Закрывать",
+ "title": "Подробности бота"
+ },
"witch": {
"desc": "Вызов ведьмы"
}
diff --git a/frontend/app/src/i18n/uk.json b/frontend/app/src/i18n/uk.json
index 71d35e5a09..6c560e38f1 100644
--- a/frontend/app/src/i18n/uk.json
+++ b/frontend/app/src/i18n/uk.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Опублікуйте посилання, щоб пояснити Діамантове членство"
},
+ "edit": {
+ "failure": "На жаль, ми не змогли оновити дозволи бота",
+ "title": "Оновіть дозволи бота",
+ "updateBot": "Оновлення бота"
+ },
+ "events": {
+ "add": "Бот **{botname}** додав {username}",
+ "remove": "Бот **{botname}** видалено {username}",
+ "update": "Бот **{botname}** оновлено {username}"
+ },
"explorer": {
"addBots": "Додайте ботів",
"loadMore": "Завантажити більше..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Видалити бота",
+ "removeFailed": "На жаль, ми не змогли видалити бота",
+ "review": "Перегляньте дозволи бота",
+ "view": "Переглянути деталі бота"
+ },
"matchingCommands": "Зіставлення команд",
+ "member": {
+ "bots": "Боти",
+ "people": "Люди"
+ },
"poll": {
"desc": "Створити опитування в поточному чаті"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Розпочніть пропозицію щодо оновлення існуючого бота OpenChat"
},
+ "view": {
+ "close": "Закрити",
+ "title": "Деталі бота"
+ },
"witch": {
"desc": "Викликати відьму"
}
diff --git a/frontend/app/src/i18n/vi.json b/frontend/app/src/i18n/vi.json
index bf202ad2e9..4b3429c8be 100644
--- a/frontend/app/src/i18n/vi.json
+++ b/frontend/app/src/i18n/vi.json
@@ -244,6 +244,16 @@
"diamond": {
"desc": "Đăng liên kết để giải thích về tư cách thành viên Kim cương"
},
+ "edit": {
+ "failure": "Xin lỗi, chúng tôi không thể cập nhật quyền của bot",
+ "title": "Cập nhật quyền bot",
+ "updateBot": "Cập nhật bot"
+ },
+ "events": {
+ "add": "Bot **{botname}** được thêm vào bởi {username}",
+ "remove": "Bot **{botname}** đã bị xóa bởi {username}",
+ "update": "Bot **{botname}** được cập nhật bởi {username}"
+ },
"explorer": {
"addBots": "Thêm bot",
"loadMore": "Tải thêm..."
@@ -287,7 +297,17 @@
}
}
},
+ "manage": {
+ "remove": "Xóa bot",
+ "removeFailed": "Xin lỗi chúng tôi không thể xóa bot",
+ "review": "Xem lại quyền của bot",
+ "view": "Xem chi tiết bot"
+ },
"matchingCommands": "Lệnh khớp",
+ "member": {
+ "bots": "Bots",
+ "people": "Mọi người"
+ },
"poll": {
"desc": "Tạo một cuộc thăm dò trong cuộc trò chuyện hiện tại"
},
@@ -307,6 +327,10 @@
"update_bot": {
"desc": "Bắt đầu đề xuất cập nhật bot OpenChat hiện có"
},
+ "view": {
+ "close": "Đóng",
+ "title": "Chi tiết bot"
+ },
"witch": {
"desc": "Triệu hồi phù thủy"
}
diff --git a/frontend/openchat-agent/src/services/common/chatMappersV2.ts b/frontend/openchat-agent/src/services/common/chatMappersV2.ts
index 5025f061ba..7d32a0ee95 100644
--- a/frontend/openchat-agent/src/services/common/chatMappersV2.ts
+++ b/frontend/openchat-agent/src/services/common/chatMappersV2.ts
@@ -120,6 +120,7 @@ import type {
SetPinNumberResponse,
MessagePermission,
SlashCommandPermissions,
+ BotGroupDetails,
} from "openchat-shared";
import {
ProposalDecisionStatus,
@@ -284,6 +285,13 @@ import type {
CommunityPermission,
MessagePermission as ApiMessagePermission,
SlashCommandPermissions as ApiSlashCommandPermissions,
+ CommunityRemoveBotResponse,
+ CommunityAddBotResponse,
+ CommunityUpdateBotResponse,
+ GroupRemoveBotResponse,
+ GroupAddBotResponse,
+ GroupUpdateBotResponse,
+ BotGroupDetails as ApiBotGroupDetails,
} from "../../typebox";
const E8S_AS_BIGINT = BigInt(100_000_000);
@@ -2607,6 +2615,7 @@ export function groupDetailsResponse(
});
}
}
+ const bots = "bots" in value.Success ? value.Success.bots : [];
return {
members,
blockedUsers: new Set(value.Success.blocked_users.map(principalBytesToString)),
@@ -2614,6 +2623,7 @@ export function groupDetailsResponse(
pinnedMessages: new Set(value.Success.pinned_messages),
rules: value.Success.chat_rules,
timestamp: value.Success.timestamp,
+ bots: bots.map(botGroupDetails),
};
}
throw new UnsupportedValueError("Unexpected ApiDeleteMessageResponse type received", value);
@@ -2642,6 +2652,8 @@ export function groupDetailsUpdatesResponse(
(invited_users) => new Set(invited_users.map(principalBytesToString)),
),
timestamp: value.Success.timestamp,
+ botsAddedOrUpdated: value.Success.bots_added_or_updated.map(botGroupDetails),
+ botsRemoved: new Set(value.Success.bots_removed.map(principalBytesToString)),
};
} else if ("SuccessNoUpdates" in value) {
return {
@@ -3316,3 +3328,38 @@ export function slashCommandPermissions(
messagePermissions: value.message.map(messagePermission),
};
}
+
+export function removeBotResponse(
+ value: CommunityRemoveBotResponse | GroupRemoveBotResponse,
+): boolean {
+ if (value === "Success") {
+ return true;
+ }
+ console.warn("Community|GroupRemoveBotResponse failed with ", value);
+ return false;
+}
+
+export function addBotResponse(value: CommunityAddBotResponse | GroupAddBotResponse): boolean {
+ if (value === "Success" || value === "AlreadyAdded") {
+ return true;
+ }
+ console.warn("Community|GroupAddBotResponse failed with ", value);
+ return false;
+}
+
+export function updateBotResponse(
+ value: CommunityUpdateBotResponse | GroupUpdateBotResponse,
+): boolean {
+ if (value === "Success") {
+ return true;
+ }
+ console.warn("Community|GroupUpdateBotResponse failed with ", value);
+ return false;
+}
+
+export function botGroupDetails(value: ApiBotGroupDetails): BotGroupDetails {
+ return {
+ id: principalBytesToString(value.user_id),
+ permissions: slashCommandPermissions(value.permissions),
+ };
+}
diff --git a/frontend/openchat-agent/src/services/community/community.client.ts b/frontend/openchat-agent/src/services/community/community.client.ts
index 144c3856e0..a782e8e2be 100644
--- a/frontend/openchat-agent/src/services/community/community.client.ts
+++ b/frontend/openchat-agent/src/services/community/community.client.ts
@@ -10,7 +10,6 @@ import {
} from "../../utils/mapping";
import type { AgentConfig } from "../../config";
import {
- addBotResponse,
addMembersToChannelResponse,
apiCommunityRole,
apiMemberRole,
@@ -25,7 +24,6 @@ import {
exploreChannelsResponse,
followThreadResponse,
importGroupResponse,
- removeBotResponse,
removeMemberFromChannelResponse,
removeMemberResponse,
reportMessageResponse,
@@ -75,6 +73,9 @@ import {
apiChatPermission,
apiCommunityPermission,
apiMessagePermission,
+ addBotResponse,
+ updateBotResponse,
+ removeBotResponse,
} from "../common/chatMappersV2";
import type {
AddMembersToChannelResponse,
@@ -295,6 +296,8 @@ import {
CommunityAddBotResponse,
CommunityRemoveBotArgs,
CommunityRemoveBotResponse,
+ CommunityUpdateBotArgs,
+ CommunityUpdateBotResponse,
} from "../../typebox";
export class CommunityClient extends CandidService {
@@ -1738,9 +1741,27 @@ export class CommunityClient extends CandidService {
);
}
+ updateBot(botId: string, grantedPermissions: SlashCommandPermissions): Promise {
+ return this.executeMsgpackUpdate(
+ "update_bot",
+ {
+ bot_id: principalStringToBytes(botId),
+ granted_permissions: {
+ chat: grantedPermissions.chatPermissions.map(apiChatPermission),
+ community: grantedPermissions.communityPermissions.map(apiCommunityPermission),
+ message: grantedPermissions.messagePermissions.map(apiMessagePermission),
+ thread: grantedPermissions.messagePermissions.map(apiMessagePermission),
+ },
+ },
+ updateBotResponse,
+ CommunityUpdateBotArgs,
+ CommunityUpdateBotResponse,
+ );
+ }
+
removeBot(botId: string): Promise {
return this.executeMsgpackUpdate(
- "add_bot",
+ "remove_bot",
{
bot_id: principalStringToBytes(botId),
},
diff --git a/frontend/openchat-agent/src/services/community/mappersV2.ts b/frontend/openchat-agent/src/services/community/mappersV2.ts
index 8426abc50b..a3070feb42 100644
--- a/frontend/openchat-agent/src/services/community/mappersV2.ts
+++ b/frontend/openchat-agent/src/services/community/mappersV2.ts
@@ -1,7 +1,6 @@
import type {
AddMembersToChannelResponse,
BlockCommunityUserResponse,
- BotGroupDetails,
ChangeCommunityRoleResponse,
ChannelMatch,
ChannelSummaryResponse,
@@ -65,14 +64,12 @@ import type {
CommunitySetMemberDisplayNameResponse,
CommunityDeleteUserGroupsResponse,
CommunityUpdateUserGroupResponse,
- CommunityAddBotResponse,
- CommunityRemoveBotResponse,
- BotGroupDetails as ApiBotGroupDetails,
} from "../../typebox";
import { mapOptional, optionUpdateV2, principalBytesToString } from "../../utils/mapping";
import {
accessGateConfig,
apiCommunityPermissionRole,
+ botGroupDetails,
chatMetrics,
communityChannelSummary,
communityPermissions,
@@ -82,7 +79,6 @@ import {
memberRole,
mentions,
messageEvent,
- slashCommandPermissions,
threadSyncDetails,
updatedEvent,
userGroup,
@@ -90,22 +86,6 @@ import {
import { identity } from "../../utils/mapping";
import { mapCommonResponses } from "../common/commonResponseMapper";
-export function removeBotResponse(value: CommunityRemoveBotResponse): boolean {
- if (value === "Success") {
- return true;
- }
- console.warn("CommunityRemoveBotResponse failed with ", value);
- return false;
-}
-
-export function addBotResponse(value: CommunityAddBotResponse): boolean {
- if (value === "Success" || value === "AlreadyAdded") {
- return true;
- }
- console.warn("CommunityAddBotResponse failed with ", value);
- return false;
-}
-
export function addMembersToChannelResponse(
value: CommunityAddMembersToChannelResponse,
): AddMembersToChannelResponse {
@@ -496,13 +476,6 @@ export function communityDetailsResponse(
}
}
-export function botGroupDetails(value: ApiBotGroupDetails): BotGroupDetails {
- return {
- id: principalBytesToString(value.user_id),
- permissions: slashCommandPermissions(value.permissions),
- };
-}
-
export function userGroupDetails(value: TUserGroupDetails): [number, UserGroupDetails] {
return [
value.user_group_id,
diff --git a/frontend/openchat-agent/src/services/group/group.client.ts b/frontend/openchat-agent/src/services/group/group.client.ts
index df254292ef..4835a0a83f 100644
--- a/frontend/openchat-agent/src/services/group/group.client.ts
+++ b/frontend/openchat-agent/src/services/group/group.client.ts
@@ -50,6 +50,7 @@ import type {
VideoCallPresence,
VideoCallParticipantsResponse,
AccessGateConfig,
+ SlashCommandPermissions,
} from "openchat-shared";
import {
DestinationInvalidError,
@@ -91,9 +92,13 @@ import {
} from "../../utils/caching";
import {
acceptP2PSwapResponse,
+ addBotResponse,
addRemoveReactionResponse,
apiAccessGateConfig,
+ apiChatPermission,
+ apiCommunityPermission,
apiMessageContent,
+ apiMessagePermission,
apiUser as apiUserV2,
apiVideoCallPresence,
cancelP2PSwapResponse,
@@ -112,11 +117,13 @@ import {
pinMessageResponse,
registerPollVoteResponse,
registerProposalVoteResponse,
+ removeBotResponse,
searchGroupChatResponse,
setVideoCallPresence,
threadPreviewsResponse,
undeleteMessageResponse,
unpinMessageResponse,
+ updateBotResponse,
updateGroupResponse,
videoCallParticipantsResponse,
} from "../common/chatMappersV2";
@@ -222,6 +229,12 @@ import {
GroupUpdateGroupResponse,
GroupVideoCallParticipantsArgs,
GroupVideoCallParticipantsResponse,
+ GroupAddBotArgs,
+ GroupAddBotResponse,
+ GroupUpdateBotArgs,
+ GroupUpdateBotResponse,
+ GroupRemoveBotArgs,
+ GroupRemoveBotResponse,
} from "../../typebox";
export class GroupClient extends CandidService {
@@ -1277,4 +1290,52 @@ export class GroupClient extends CandidService {
GroupCancelInvitesResponse,
);
}
+
+ addBot(botId: string, grantedPermissions: SlashCommandPermissions): Promise {
+ return this.executeMsgpackUpdate(
+ "add_bot",
+ {
+ bot_id: principalStringToBytes(botId),
+ granted_permissions: {
+ chat: grantedPermissions.chatPermissions.map(apiChatPermission),
+ community: grantedPermissions.communityPermissions.map(apiCommunityPermission),
+ message: grantedPermissions.messagePermissions.map(apiMessagePermission),
+ thread: grantedPermissions.messagePermissions.map(apiMessagePermission),
+ },
+ },
+ addBotResponse,
+ GroupAddBotArgs,
+ GroupAddBotResponse,
+ );
+ }
+
+ updateBot(botId: string, grantedPermissions: SlashCommandPermissions): Promise {
+ return this.executeMsgpackUpdate(
+ "update_bot",
+ {
+ bot_id: principalStringToBytes(botId),
+ granted_permissions: {
+ chat: grantedPermissions.chatPermissions.map(apiChatPermission),
+ community: grantedPermissions.communityPermissions.map(apiCommunityPermission),
+ message: grantedPermissions.messagePermissions.map(apiMessagePermission),
+ thread: grantedPermissions.messagePermissions.map(apiMessagePermission),
+ },
+ },
+ updateBotResponse,
+ GroupUpdateBotArgs,
+ GroupUpdateBotResponse,
+ );
+ }
+
+ removeBot(botId: string): Promise {
+ return this.executeMsgpackUpdate(
+ "remove_bot",
+ {
+ bot_id: principalStringToBytes(botId),
+ },
+ removeBotResponse,
+ GroupRemoveBotArgs,
+ GroupRemoveBotResponse,
+ );
+ }
}
diff --git a/frontend/openchat-agent/src/services/openchatAgent.ts b/frontend/openchat-agent/src/services/openchatAgent.ts
index 47d092046b..ecab0ef0cb 100644
--- a/frontend/openchat-agent/src/services/openchatAgent.ts
+++ b/frontend/openchat-agent/src/services/openchatAgent.ts
@@ -4019,16 +4019,39 @@ export class OpenChatAgent extends EventTarget {
return this._userIndexClient.registerBot(bot);
}
- addBotToCommunity(
- id: CommunityIdentifier,
+ addBot(
+ id: CommunityIdentifier | GroupChatIdentifier,
+ botId: string,
+ grantedPermissions: SlashCommandPermissions,
+ ): Promise {
+ switch (id.kind) {
+ case "community":
+ return this.communityClient(id.communityId).addBot(botId, grantedPermissions);
+ case "group_chat":
+ return this.getGroupClient(id.groupId).addBot(botId, grantedPermissions);
+ }
+ }
+
+ updateBot(
+ id: CommunityIdentifier | GroupChatIdentifier,
botId: string,
grantedPermissions: SlashCommandPermissions,
): Promise {
- return this.communityClient(id.communityId).addBot(botId, grantedPermissions);
+ switch (id.kind) {
+ case "community":
+ return this.communityClient(id.communityId).updateBot(botId, grantedPermissions);
+ case "group_chat":
+ return this.getGroupClient(id.groupId).updateBot(botId, grantedPermissions);
+ }
}
- removeBotFromCommunity(id: CommunityIdentifier, botId: string): Promise {
- return this.communityClient(id.communityId).removeBot(botId);
+ removeBot(id: CommunityIdentifier | GroupChatIdentifier, botId: string): Promise {
+ switch (id.kind) {
+ case "community":
+ return this.communityClient(id.communityId).removeBot(botId);
+ case "group_chat":
+ return this.getGroupClient(id.groupId).removeBot(botId);
+ }
}
getBots(initialLoad: boolean): Stream {
diff --git a/frontend/openchat-agent/src/services/userIndex/mappers.ts b/frontend/openchat-agent/src/services/userIndex/mappers.ts
index 1082c57d3b..4a2d082a7d 100644
--- a/frontend/openchat-agent/src/services/userIndex/mappers.ts
+++ b/frontend/openchat-agent/src/services/userIndex/mappers.ts
@@ -699,8 +699,7 @@ export function externalBotMatch(
.replace("{blobType}", "avatar")}/${botId}/${id}`,
),
id: botId,
- score: match.score,
- owner: principalBytesToString(match.owner),
+ ownerId: principalBytesToString(match.owner),
description: match.description,
commands: match.commands.map(externalBotCommand),
};
diff --git a/frontend/openchat-agent/src/typebox.ts b/frontend/openchat-agent/src/typebox.ts
index fef552f21e..b4d07d3c6c 100644
--- a/frontend/openchat-agent/src/typebox.ts
+++ b/frontend/openchat-agent/src/typebox.ts
@@ -180,6 +180,12 @@ export const GroupDisableInviteCodeArgs = Type.Object({
correlation_id: Type.BigInt(),
});
+export type GroupRemoveBotResponse = Static;
+export const GroupRemoveBotResponse = Type.Union([
+ Type.Literal("Success"),
+ Type.Literal("NotAuthorized"),
+]);
+
export type GroupRegisterProposalVoteV2Response = Static<
typeof GroupRegisterProposalVoteV2Response
>;
@@ -271,6 +277,14 @@ export const GroupDeclineInvitiationResponse = Type.Union([
Type.Literal("NotInvited"),
]);
+export type GroupUpdateBotResponse = Static;
+export const GroupUpdateBotResponse = Type.Union([
+ Type.Literal("Success"),
+ Type.Literal("ChatFrozen"),
+ Type.Literal("NotAuthorized"),
+ Type.Literal("NotFound"),
+]);
+
export type GroupSelectedUpdatesArgs = Static;
export const GroupSelectedUpdatesArgs = Type.Object({
updates_since: Type.BigInt(),
@@ -315,6 +329,14 @@ export const GroupFollowThreadResponse = Type.Union([
Type.Literal("GroupFrozen"),
]);
+export type GroupAddBotResponse = Static;
+export const GroupAddBotResponse = Type.Union([
+ Type.Literal("Success"),
+ Type.Literal("ChatFrozen"),
+ Type.Literal("NotAuthorized"),
+ Type.Literal("AlreadyAdded"),
+]);
+
export type ChannelId = Static;
export const ChannelId = Type.BigInt();
@@ -3037,6 +3059,14 @@ export const CommunityEventsArgs = Type.Object({
latest_known_update: Type.Optional(Type.Union([Type.BigInt(), Type.Undefined()])),
});
+export type CommunityUpdateBotResponse = Static;
+export const CommunityUpdateBotResponse = Type.Union([
+ Type.Literal("Success"),
+ Type.Literal("CommunityFrozen"),
+ Type.Literal("NotAuthorized"),
+ Type.Literal("NotFound"),
+]);
+
export type CommunityAcceptP2pSwapArgs = Static;
export const CommunityAcceptP2pSwapArgs = Type.Object({
channel_id: ChannelId,
@@ -3181,7 +3211,7 @@ export const CommunityFollowThreadArgs = Type.Object({
export type CommunityAddBotResponse = Static;
export const CommunityAddBotResponse = Type.Union([
Type.Literal("Success"),
- Type.Literal("ChatFrozen"),
+ Type.Literal("CommunityFrozen"),
Type.Literal("NotAuthorized"),
Type.Literal("AlreadyAdded"),
]);
@@ -5206,6 +5236,12 @@ export const CommunityBlockUserArgs = Type.Object({
user_id: UserId,
});
+export type CommunityUpdateBotArgs = Static;
+export const CommunityUpdateBotArgs = Type.Object({
+ bot_id: UserId,
+ granted_permissions: SlashCommandPermissions,
+});
+
export type CommunityCreateChannelResponse = Static;
export const CommunityCreateChannelResponse = Type.Union([
Type.Object({
@@ -5404,6 +5440,11 @@ export const GroupUpdateGroupResponse = Type.Union([
Type.Literal("InternalError"),
]);
+export type GroupRemoveBotArgs = Static;
+export const GroupRemoveBotArgs = Type.Object({
+ bot_id: UserId,
+});
+
export type GroupBlockUserArgs = Static;
export const GroupBlockUserArgs = Type.Object({
user_id: UserId,
@@ -5416,6 +5457,18 @@ export const GroupRemoveParticipantArgs = Type.Object({
correlation_id: Type.BigInt(),
});
+export type GroupUpdateBotArgs = Static;
+export const GroupUpdateBotArgs = Type.Object({
+ bot_id: UserId,
+ granted_permissions: SlashCommandPermissions,
+});
+
+export type GroupAddBotArgs = Static;
+export const GroupAddBotArgs = Type.Object({
+ bot_id: UserId,
+ granted_permissions: SlashCommandPermissions,
+});
+
export type UserSearchMessagesArgs = Static;
export const UserSearchMessagesArgs = Type.Object({
user_id: UserId,
diff --git a/frontend/openchat-agent/src/utils/caching.ts b/frontend/openchat-agent/src/utils/caching.ts
index cc584d564d..10eec4281a 100644
--- a/frontend/openchat-agent/src/utils/caching.ts
+++ b/frontend/openchat-agent/src/utils/caching.ts
@@ -255,7 +255,12 @@ const migrations: Record> = {
createBotsStore(db, principal, tx),
]);
},
- 121: clearEvents,
+ 121: async (db, principal, tx) => {
+ await Promise.all([
+ clearEvents(db, principal, tx),
+ clearGroupDetailsStore(db, principal, tx),
+ ]);
+ },
};
async function migrate(
diff --git a/frontend/openchat-agent/src/utils/chat.ts b/frontend/openchat-agent/src/utils/chat.ts
index 618a0a0617..9bd10c4733 100644
--- a/frontend/openchat-agent/src/utils/chat.ts
+++ b/frontend/openchat-agent/src/utils/chat.ts
@@ -102,11 +102,16 @@ export function mergeCommunityDetails(
updates.userGroups,
updates.userGroupsDeleted,
),
- bots: mergeThings((b) => b.id, identity, previous.bots, {
- added: [],
- updated: updates.botsAddedOrUpdated,
- removed: updates.botsRemoved,
- }),
+ bots: mergeThings(
+ (b) => b.id,
+ (_, updated) => updated,
+ previous.bots,
+ {
+ added: [],
+ updated: updates.botsAddedOrUpdated,
+ removed: updates.botsRemoved,
+ },
+ ),
};
}
@@ -145,6 +150,16 @@ export function mergeGroupChatDetails(
updates.pinnedMessagesRemoved,
),
rules: updates.rules ?? previous.rules,
+ bots: mergeThings(
+ (b) => b.id,
+ (_, updated) => updated,
+ previous.bots,
+ {
+ added: [],
+ updated: updates.botsAddedOrUpdated,
+ removed: updates.botsRemoved,
+ },
+ ),
};
}
diff --git a/frontend/openchat-client/src/openchat.ts b/frontend/openchat-client/src/openchat.ts
index 3bd3de822e..1b8d951e9c 100644
--- a/frontend/openchat-client/src/openchat.ts
+++ b/frontend/openchat-client/src/openchat.ts
@@ -1924,6 +1924,17 @@ export class OpenChat extends EventTarget {
}
}
+ canManageBots(id: ChatIdentifier | CommunityIdentifier): boolean {
+ switch (id.kind) {
+ case "community":
+ return this.#communityPredicate(id, ({ membership: { role } }) =>
+ hasOwnerRights(role),
+ );
+ default:
+ return this.#chatPredicate(id, ({ membership: { role } }) => hasOwnerRights(role));
+ }
+ }
+
canAddMembers(id: ChatIdentifier): boolean {
return this.#chatPredicate(id, canAddMembers);
}
@@ -3159,6 +3170,11 @@ export class OpenChat extends EventTarget {
chatStateStore.setProp(serverChat.id, "invitedUsers", resp.invitedUsers);
chatStateStore.setProp(serverChat.id, "pinnedMessages", resp.pinnedMessages);
chatStateStore.setProp(serverChat.id, "rules", resp.rules);
+ chatStateStore.setProp(
+ serverChat.id,
+ "bots",
+ resp.bots.reduce((all, b) => all.set(b.id, b.permissions), new Map()),
+ );
}
await this.#updateUserStoreFromEvents(serverChat.id, []);
}
@@ -7823,29 +7839,46 @@ export class OpenChat extends EventTarget {
});
}
- addBotToCommunity(
- id: CommunityIdentifier,
+ addBot(
+ id: CommunityIdentifier | GroupChatIdentifier,
+ botId: string,
+ grantedPermissions: SlashCommandPermissions,
+ ): Promise {
+ return this.#sendRequest({
+ kind: "addBot",
+ id,
+ botId,
+ grantedPermissions,
+ }).catch((err) => {
+ this.#logger.error("Error adding bot to group or community", err);
+ return false;
+ });
+ }
+
+ updateBot(
+ id: CommunityIdentifier | GroupChatIdentifier,
botId: string,
grantedPermissions: SlashCommandPermissions,
): Promise {
return this.#sendRequest({
- kind: "addBotToCommunity",
+ kind: "updateBot",
id,
botId,
grantedPermissions,
}).catch((err) => {
- this.#logger.error("Error adding bot to community", err);
+ this.#logger.error("Error adding bot to group or community", err);
return false;
});
}
- removeBotFromCommunity(id: CommunityIdentifier, botId: string): Promise {
+ // TODO - probably need to think about local updates here
+ removeBot(id: CommunityIdentifier | GroupChatIdentifier, botId: string): Promise {
return this.#sendRequest({
- kind: "removeBotFromCommunity",
+ kind: "removeBot",
id,
botId,
}).catch((err) => {
- this.#logger.error("Error removing bot from community", err);
+ this.#logger.error("Error removing bot from group or community", err);
return false;
});
}
diff --git a/frontend/openchat-client/src/stores/chat.ts b/frontend/openchat-client/src/stores/chat.ts
index 24a6c6bcbc..20e9d41eb3 100644
--- a/frontend/openchat-client/src/stores/chat.ts
+++ b/frontend/openchat-client/src/stores/chat.ts
@@ -12,6 +12,7 @@ import type {
ChatListScope,
ExpiredEventsRange,
MessageContext,
+ SlashCommandPermissions,
} from "openchat-shared";
import {
compareChats,
@@ -89,6 +90,7 @@ export const chatStateStore = createChatSpecificObjectStore(
serverEvents: [],
expandedDeletedMessages: new Set(),
expiredEventRanges: new DRange(),
+ bots: new Map(),
}),
);
@@ -98,6 +100,12 @@ const serverEventsStore = createDerivedPropStore [],
);
+export const currentChatBots = createDerivedPropStore(
+ chatStateStore,
+ "bots",
+ () => new Map(),
+);
+
export const currentChatUserIds = createDerivedPropStore(
chatStateStore,
"userIds",
diff --git a/frontend/openchat-client/src/utils/testBots.ts b/frontend/openchat-client/src/utils/testBots.ts
index 876004abc5..934efc469f 100644
--- a/frontend/openchat-client/src/utils/testBots.ts
+++ b/frontend/openchat-client/src/utils/testBots.ts
@@ -216,19 +216,17 @@ export const testBots: Bot[] = [
export const testMatches: BotMatch[] = [
{
id: "one",
- score: 10,
name: "Kitten bot",
description: "this is just a test bot and it doen't do very much",
- owner: "owner",
+ ownerId: "owner",
commands: [killCommand, kickPollCommand],
},
{
id: "two",
- score: 10,
name: "Puppy bot",
description:
"This bot also does not do anything but in this case it has a much longer description. The reason that we need a longer description is so that we can tell that the card still renders ok if there is a lot of text to display. What should we do? Should we truncate it or should we do something else? Show multiple lines? Show the whole thing? Make it expandable?",
- owner: "owner",
+ ownerId: "owner",
commands: [
chatCommand,
banCommand,
diff --git a/frontend/openchat-shared/src/domain/bots.ts b/frontend/openchat-shared/src/domain/bots.ts
index 7b7a8b7b5b..e5f1e289eb 100644
--- a/frontend/openchat-shared/src/domain/bots.ts
+++ b/frontend/openchat-shared/src/domain/bots.ts
@@ -196,7 +196,7 @@ export type ExternalBot = {
id: string;
ownerId: string;
endpoint: string;
- description?: string;
+ description: string;
commands: SlashCommandSchema[];
};
diff --git a/frontend/openchat-shared/src/domain/chat/chat.ts b/frontend/openchat-shared/src/domain/chat/chat.ts
index a2af2082cc..185d3f7abc 100644
--- a/frontend/openchat-shared/src/domain/chat/chat.ts
+++ b/frontend/openchat-shared/src/domain/chat/chat.ts
@@ -35,6 +35,7 @@ import type {
} from "../community";
import type { ChitEarned } from "../chit";
import type { WalletConfig } from "../crypto";
+import type { BotGroupDetails, SlashCommandPermissions } from "../bots";
export type CallerNotInGroup = { kind: "caller_not_in_group" };
export type CanisterNotFound = { kind: "canister_not_found" };
@@ -1409,6 +1410,7 @@ export type GroupChatDetails = {
pinnedMessages: Set;
rules: VersionedRules;
timestamp: bigint;
+ bots: BotGroupDetails[];
};
/**
@@ -1431,6 +1433,7 @@ export type ChatSpecificState = {
serverEvents: EventWrapper[];
expandedDeletedMessages: Set;
expiredEventRanges: DRange;
+ bots: Map;
};
export type GroupChatDetailsUpdates = {
@@ -1443,6 +1446,8 @@ export type GroupChatDetailsUpdates = {
rules?: VersionedRules;
invitedUsers?: Set;
timestamp: bigint;
+ botsAddedOrUpdated: BotGroupDetails[];
+ botsRemoved: Set;
};
export type ChatSummary = DirectChatSummary | MultiUserChat;
diff --git a/frontend/openchat-shared/src/domain/search/search.ts b/frontend/openchat-shared/src/domain/search/search.ts
index 329ab7f150..c9b42ffda1 100644
--- a/frontend/openchat-shared/src/domain/search/search.ts
+++ b/frontend/openchat-shared/src/domain/search/search.ts
@@ -33,10 +33,9 @@ export type MessageMatch = {
export type BotMatch = {
id: string;
- score: number;
name: string;
description: string;
- owner: string;
+ ownerId: string;
avatarUrl?: string;
commands: SlashCommandSchema[];
};
diff --git a/frontend/openchat-shared/src/domain/worker.ts b/frontend/openchat-shared/src/domain/worker.ts
index 4fdc811b6e..f51e9e61ac 100644
--- a/frontend/openchat-shared/src/domain/worker.ts
+++ b/frontend/openchat-shared/src/domain/worker.ts
@@ -412,19 +412,27 @@ export type WorkerRequest =
| MessageActivityFeed
| MarkActivityFeedRead
| DeleteUser
- | AddBotToCommunity
- | RemoveBotFromCommunity;
+ | AddBot
+ | RemoveBot
+ | UpdateBot;
-type AddBotToCommunity = {
- kind: "addBotToCommunity";
- id: CommunityIdentifier;
+type AddBot = {
+ kind: "addBot";
+ id: CommunityIdentifier | GroupChatIdentifier;
botId: string;
grantedPermissions: SlashCommandPermissions;
};
-type RemoveBotFromCommunity = {
- kind: "removeBotFromCommunity";
- id: CommunityIdentifier;
+type UpdateBot = {
+ kind: "updateBot";
+ id: CommunityIdentifier | GroupChatIdentifier;
+ botId: string;
+ grantedPermissions: SlashCommandPermissions;
+};
+
+type RemoveBot = {
+ kind: "removeBot";
+ id: CommunityIdentifier | GroupChatIdentifier;
botId: string;
};
@@ -2248,6 +2256,10 @@ export type WorkerResult = T extends Init
? void
: T extends DeleteUser
? boolean
- : T extends AddBotToCommunity
+ : T extends AddBot
+ ? boolean
+ : T extends RemoveBot
+ ? boolean
+ : T extends UpdateBot
? boolean
: never;
diff --git a/frontend/openchat-worker/src/worker.ts b/frontend/openchat-worker/src/worker.ts
index abd5af5261..1efa20bf36 100644
--- a/frontend/openchat-worker/src/worker.ts
+++ b/frontend/openchat-worker/src/worker.ts
@@ -1867,19 +1867,27 @@ self.addEventListener("message", (msg: MessageEvent) =>
executeThenReply(payload, correlationId, agent.deleteUser(payload.userId));
break;
- case "addBotToCommunity":
+ case "addBot":
executeThenReply(
payload,
correlationId,
- agent.addBotToCommunity(payload.id, payload.botId, payload.grantedPermissions),
+ agent.addBot(payload.id, payload.botId, payload.grantedPermissions),
);
break;
- case "removeBotFromCommunity":
+ case "updateBot":
executeThenReply(
payload,
correlationId,
- agent.removeBotFromCommunity(payload.id, payload.botId),
+ agent.updateBot(payload.id, payload.botId, payload.grantedPermissions),
+ );
+ break;
+
+ case "removeBot":
+ executeThenReply(
+ payload,
+ correlationId,
+ agent.removeBot(payload.id, payload.botId),
);
break;
diff --git a/tsBindings/community/addBot/CommunityAddBotResponse.ts b/tsBindings/community/addBot/CommunityAddBotResponse.ts
index efefb10f45..30861e67d1 100644
--- a/tsBindings/community/addBot/CommunityAddBotResponse.ts
+++ b/tsBindings/community/addBot/CommunityAddBotResponse.ts
@@ -1,3 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
-export type CommunityAddBotResponse = "Success" | "ChatFrozen" | "NotAuthorized" | "AlreadyAdded";
+export type CommunityAddBotResponse = "Success" | "CommunityFrozen" | "NotAuthorized" | "AlreadyAdded";
diff --git a/tsBindings/community/updateBot/CommunityUpdateBotArgs.ts b/tsBindings/community/updateBot/CommunityUpdateBotArgs.ts
new file mode 100644
index 0000000000..a1b0e84e4d
--- /dev/null
+++ b/tsBindings/community/updateBot/CommunityUpdateBotArgs.ts
@@ -0,0 +1,5 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+import type { SlashCommandPermissions } from "../../shared/SlashCommandPermissions";
+import type { UserId } from "../../shared/UserId";
+
+export type CommunityUpdateBotArgs = { bot_id: UserId, granted_permissions: SlashCommandPermissions, };
diff --git a/tsBindings/community/updateBot/CommunityUpdateBotResponse.ts b/tsBindings/community/updateBot/CommunityUpdateBotResponse.ts
new file mode 100644
index 0000000000..8c951aa9fd
--- /dev/null
+++ b/tsBindings/community/updateBot/CommunityUpdateBotResponse.ts
@@ -0,0 +1,3 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+
+export type CommunityUpdateBotResponse = "Success" | "CommunityFrozen" | "NotAuthorized" | "NotFound";
diff --git a/tsBindings/group/addBot/GroupAddBotArgs.ts b/tsBindings/group/addBot/GroupAddBotArgs.ts
new file mode 100644
index 0000000000..c055dcfebc
--- /dev/null
+++ b/tsBindings/group/addBot/GroupAddBotArgs.ts
@@ -0,0 +1,5 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+import type { SlashCommandPermissions } from "../../shared/SlashCommandPermissions";
+import type { UserId } from "../../shared/UserId";
+
+export type GroupAddBotArgs = { bot_id: UserId, granted_permissions: SlashCommandPermissions, };
diff --git a/tsBindings/group/addBot/GroupAddBotResponse.ts b/tsBindings/group/addBot/GroupAddBotResponse.ts
new file mode 100644
index 0000000000..0a81a9f8bf
--- /dev/null
+++ b/tsBindings/group/addBot/GroupAddBotResponse.ts
@@ -0,0 +1,3 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+
+export type GroupAddBotResponse = "Success" | "ChatFrozen" | "NotAuthorized" | "AlreadyAdded";
diff --git a/tsBindings/group/removeBot/GroupRemoveBotArgs.ts b/tsBindings/group/removeBot/GroupRemoveBotArgs.ts
new file mode 100644
index 0000000000..62854f13b4
--- /dev/null
+++ b/tsBindings/group/removeBot/GroupRemoveBotArgs.ts
@@ -0,0 +1,4 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+import type { UserId } from "../../shared/UserId";
+
+export type GroupRemoveBotArgs = { bot_id: UserId, };
diff --git a/tsBindings/group/removeBot/GroupRemoveBotResponse.ts b/tsBindings/group/removeBot/GroupRemoveBotResponse.ts
new file mode 100644
index 0000000000..72d1d01b49
--- /dev/null
+++ b/tsBindings/group/removeBot/GroupRemoveBotResponse.ts
@@ -0,0 +1,3 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+
+export type GroupRemoveBotResponse = "Success" | "NotAuthorized";
diff --git a/tsBindings/group/updateBot/GroupUpdateBotArgs.ts b/tsBindings/group/updateBot/GroupUpdateBotArgs.ts
new file mode 100644
index 0000000000..0df1e6d2d7
--- /dev/null
+++ b/tsBindings/group/updateBot/GroupUpdateBotArgs.ts
@@ -0,0 +1,5 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+import type { SlashCommandPermissions } from "../../shared/SlashCommandPermissions";
+import type { UserId } from "../../shared/UserId";
+
+export type GroupUpdateBotArgs = { bot_id: UserId, granted_permissions: SlashCommandPermissions, };
diff --git a/tsBindings/group/updateBot/GroupUpdateBotResponse.ts b/tsBindings/group/updateBot/GroupUpdateBotResponse.ts
new file mode 100644
index 0000000000..f8bfe0e2f2
--- /dev/null
+++ b/tsBindings/group/updateBot/GroupUpdateBotResponse.ts
@@ -0,0 +1,3 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+
+export type GroupUpdateBotResponse = "Success" | "ChatFrozen" | "NotAuthorized" | "NotFound";
diff --git a/tsBindings/types.d.ts b/tsBindings/types.d.ts
index aaf96f40da..e385c8089e 100644
--- a/tsBindings/types.d.ts
+++ b/tsBindings/types.d.ts
@@ -18,6 +18,7 @@ export type GroupEnableInviteCodeSuccessResult = { code: bigint, };
export type GroupRegisterProposalVoteResponse = "Success" | { "AlreadyVoted": boolean } | "CallerNotInGroup" | "NoEligibleNeurons" | "ProposalMessageNotFound" | "ProposalNotFound" | "ProposalNotAcceptingVotes" | "UserSuspended" | "UserLapsed" | "ChatFrozen" | { "InternalError": string };
export type GroupDisableInviteCodeResponse = "Success" | "NotAuthorized" | "UserSuspended" | "UserLapsed" | "ChatFrozen";
export type GroupDisableInviteCodeArgs = { correlation_id: bigint, };
+export type GroupRemoveBotResponse = "Success" | "NotAuthorized";
export type GroupRegisterProposalVoteV2Response = "Success" | "CallerNotInGroup" | "ProposalMessageNotFound" | "UserSuspended" | "UserLapsed" | "ChatFrozen";
export type GroupPublicSummaryArgs = { invite_code?: bigint | undefined, };
export type GroupRemoveReactionResponse = "Success" | "NoChange" | "MessageNotFound" | "CallerNotInGroup" | "NotAuthorized" | "UserSuspended" | "UserLapsed" | "ChatFrozen";
@@ -27,11 +28,13 @@ export type GroupRulesSuccessResult = { rules?: string | undefined, };
export type GroupBlockUserResponse = "Success" | "CallerNotInGroup" | "CannotBlockSelf" | "CannotBlockUser" | "GroupNotPublic" | { "InternalError": string } | "NotAuthorized" | "UserNotInGroup" | "UserSuspended" | "UserLapsed" | "ChatFrozen";
export type GroupRemoveParticipantResponse = "Success" | "CallerNotInGroup" | "CannotRemoveSelf" | "CannotRemoveUser" | { "InternalError": string } | "NotAuthorized" | "UserNotInGroup" | "UserSuspended" | "UserLapsed" | "ChatFrozen";
export type GroupDeclineInvitiationResponse = "Success" | "NotInvited";
+export type GroupUpdateBotResponse = "Success" | "ChatFrozen" | "NotAuthorized" | "NotFound";
export type GroupSelectedUpdatesArgs = { updates_since: bigint, };
export type GroupReportMessageResponse = "Success" | "UserSuspended" | "UserLapsed" | "ChatFrozen" | "CallerNotInGroup" | "NotAuthorized" | "MessageNotFound" | "AlreadyReported" | { "InternalError": string };
export type GroupEditMessageResponse = "Success" | "MessageNotFound" | "CallerNotInGroup" | "UserSuspended" | "UserLapsed" | "ChatFrozen";
export type GroupSummaryArgs = { };
export type GroupFollowThreadResponse = "Success" | "AlreadyFollowing" | "ThreadNotFound" | "UserNotInGroup" | "UserSuspended" | "UserLapsed" | "GroupFrozen";
+export type GroupAddBotResponse = "Success" | "ChatFrozen" | "NotAuthorized" | "AlreadyAdded";
export type ChannelId = bigint;
export type UserManageFavouriteChatsResponse = "Success" | "UserSuspended";
export type UserMessageActivitySummary = { read_up_to: bigint, latest_event_timestamp: bigint, unread_count: number, };
@@ -327,6 +330,7 @@ export type CommunitySendMessageResponse = { "Success": CommunitySendMessageSucc
export type CommunityEventsByIndexArgs = { channel_id: ChannelId, thread_root_message_index?: MessageIndex | undefined, events: Array, latest_known_update?: bigint | undefined, };
export type CommunityLocalUserIndexResponse = { "Success": TSBytes };
export type CommunityEventsArgs = { channel_id: ChannelId, thread_root_message_index?: MessageIndex | undefined, start_index: EventIndex, ascending: boolean, max_messages: number, max_events: number, latest_known_update?: bigint | undefined, };
+export type CommunityUpdateBotResponse = "Success" | "CommunityFrozen" | "NotAuthorized" | "NotFound";
export type CommunityAcceptP2pSwapArgs = { channel_id: ChannelId, thread_root_message_index?: MessageIndex | undefined, message_id: MessageId, pin?: string | undefined, new_achievement: boolean, };
export type CommunityCreateChannelSuccessResult = { channel_id: ChannelId, };
export type CommunitySelectedUpdatesArgs = { invite_code?: bigint | undefined, updates_since: bigint, };
@@ -341,7 +345,7 @@ export type CommunitySetMemberDisplayNameResponse = "Success" | "CommunityFrozen
export type CommunitySummaryArgs = { invite_code?: bigint | undefined, };
export type CommunityFollowThreadResponse = "Success" | "AlreadyFollowing" | "ThreadNotFound" | "ChannelNotFound" | "UserNotInChannel" | "UserNotInCommunity" | "UserSuspended" | "CommunityFrozen" | "UserLapsed";
export type CommunityFollowThreadArgs = { channel_id: ChannelId, thread_root_message_index: MessageIndex, new_achievement: boolean, };
-export type CommunityAddBotResponse = "Success" | "ChatFrozen" | "NotAuthorized" | "AlreadyAdded";
+export type CommunityAddBotResponse = "Success" | "CommunityFrozen" | "NotAuthorized" | "AlreadyAdded";
export type CommunityDeleteChannelResponse = "Success" | "CommunityFrozen" | "UserSuspended" | "UserNotInCommunity" | "ChannelNotFound" | "UserNotInChannel" | "NotAuthorized" | "UserLapsed";
export type CommunityDeleteChannelArgs = { channel_id: ChannelId, };
export type NotificationsIndexPushSubscriptionResponse = "Success" | { "InternalError": string };
@@ -554,6 +558,7 @@ export type CommunityAddMembersToChannelFailedResult = { users_already_in_channe
export type CommunityRemoveBotArgs = { bot_id: UserId, };
export type CommunityChangeChannelRoleArgs = { channel_id: ChannelId, user_id: UserId, new_role: GroupRole, };
export type CommunityBlockUserArgs = { user_id: UserId, };
+export type CommunityUpdateBotArgs = { bot_id: UserId, granted_permissions: SlashCommandPermissions, };
export type CommunityCreateChannelResponse = { "Success": CommunityCreateChannelSuccessResult } | { "NameTooShort": FieldTooShortResult } | { "NameTooLong": FieldTooLongResult } | "NameReserved" | { "DescriptionTooLong": FieldTooLongResult } | { "RulesTooShort": FieldTooShortResult } | { "RulesTooLong": FieldTooLongResult } | { "AvatarTooBig": FieldTooLongResult } | "AccessGateInvalid" | { "MaxChannelsCreated": number } | "NameTaken" | "UserSuspended" | "NotAuthorized" | "CommunityFrozen" | "ExternalUrlInvalid" | { "InternalError": string } | "UserLapsed";
export type CommunityImportGroupArgs = { group_id: ChatId, };
export type CommunityUpdateChannelResponse = { "SuccessV2": CommunityUpdateChannelSuccessResult } | "NotAuthorized" | "UserNotInCommunity" | "ChannelNotFound" | "UserNotInChannel" | { "NameTooShort": FieldTooShortResult } | { "NameTooLong": FieldTooLongResult } | "NameReserved" | { "DescriptionTooLong": FieldTooLongResult } | { "AvatarTooBig": FieldTooLongResult } | "AccessGateInvalid" | "NameTaken" | { "RulesTooLong": FieldTooLongResult } | { "RulesTooShort": FieldTooShortResult } | "UserSuspended" | "ExternalUrlInvalid" | "CommunityFrozen" | "UserLapsed";
@@ -569,8 +574,11 @@ export type GroupCancelInvitesArgs = { user_ids: Array, };
export type GroupChangeRoleArgs = { user_id: UserId, new_role: GroupRole, correlation_id: bigint, };
export type GroupUnblockUserArgs = { user_id: UserId, correlation_id: bigint, };
export type GroupUpdateGroupResponse = { "SuccessV2": GroupUpdateGroupSuccessResult } | "NotAuthorized" | "CallerNotInGroup" | { "NameTooShort": FieldTooShortResult } | { "NameTooLong": FieldTooLongResult } | "NameReserved" | { "DescriptionTooLong": FieldTooLongResult } | { "RulesTooShort": FieldTooShortResult } | { "RulesTooLong": FieldTooLongResult } | { "AvatarTooBig": FieldTooLongResult } | "AccessGateInvalid" | "NameTaken" | "UserSuspended" | "UserLapsed" | "ChatFrozen" | "InternalError";
+export type GroupRemoveBotArgs = { bot_id: UserId, };
export type GroupBlockUserArgs = { user_id: UserId, correlation_id: bigint, };
export type GroupRemoveParticipantArgs = { user_id: UserId, correlation_id: bigint, };
+export type GroupUpdateBotArgs = { bot_id: UserId, granted_permissions: SlashCommandPermissions, };
+export type GroupAddBotArgs = { bot_id: UserId, granted_permissions: SlashCommandPermissions, };
export type UserSearchMessagesArgs = { user_id: UserId, search_term: string, max_results: number, };
export type UserCommunitySummaryUpdates = { community_id: CommunityId, channels: Array, index?: number | undefined, archived?: boolean | undefined, pinned?: Array | undefined, };
export type UserGroupChatSummary = { chat_id: ChatId, local_user_index_canister_id: TSBytes, read_by_me_up_to?: MessageIndex | undefined, threads_read: Record, archived: boolean, date_read_pinned?: bigint | undefined, };