@coinseeker/opencode-telegram-plugin 1.0.2 → 1.0.3
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.
- package/README.md +1 -0
- package/dist/telegram-remote.js +53 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ Keep this file private. Never commit or share your Telegram bot token.
|
|
|
52
52
|
- Root session completion notifications.
|
|
53
53
|
- Background subagent-aware completion: child session messages are suppressed and parent completion waits until children finish.
|
|
54
54
|
- OpenCode question prompts via Telegram inline buttons.
|
|
55
|
+
- Multi-select question prompts with toggle buttons and **Done** submission.
|
|
55
56
|
- Custom free-text answers from Telegram.
|
|
56
57
|
- Permission alerts.
|
|
57
58
|
- Multi-session-safe Telegram polling through a file-lock leader model.
|
package/dist/telegram-remote.js
CHANGED
|
@@ -415,7 +415,7 @@ This chat is now active for OpenCode notifications.`);
|
|
|
415
415
|
logger.error("bot error", { error: String(e) });
|
|
416
416
|
}
|
|
417
417
|
});
|
|
418
|
-
bot.callbackQuery(/^q:([^:]+):(\d+):(\d+|c)$/, async (ctx) => {
|
|
418
|
+
bot.callbackQuery(/^q:([^:]+):(\d+):(\d+|c|d)$/, async (ctx) => {
|
|
419
419
|
await ctx.answerCallbackQuery();
|
|
420
420
|
const data = ctx.callbackQuery.data;
|
|
421
421
|
const messageId = ctx.callbackQuery.message?.message_id;
|
|
@@ -785,7 +785,7 @@ Detail: ${permission.title}`;
|
|
|
785
785
|
|
|
786
786
|
// src/events/question-asked.ts
|
|
787
787
|
var QUESTION_EXPIRY_MS = 5 * 6e4;
|
|
788
|
-
var CALLBACK_RE = /^q:([^:]+):(\d+):(\d+|c)$/;
|
|
788
|
+
var CALLBACK_RE = /^q:([^:]+):(\d+):(\d+|c|d)$/;
|
|
789
789
|
function isQuestionOption(value) {
|
|
790
790
|
return typeof value.label === "string" && typeof value.description === "string";
|
|
791
791
|
}
|
|
@@ -814,6 +814,26 @@ function callbackDataForQuestion(shortHash, questionIndex, question) {
|
|
|
814
814
|
if (question.custom !== false) data.push(buildCallbackData(shortHash, questionIndex, "c"));
|
|
815
815
|
return data;
|
|
816
816
|
}
|
|
817
|
+
function useSimpleQuestionKeyboard(question) {
|
|
818
|
+
return question.multiple !== true;
|
|
819
|
+
}
|
|
820
|
+
function selectedAnswers(pending, questionIndex) {
|
|
821
|
+
return pending.answersInProgress[questionIndex] ?? [];
|
|
822
|
+
}
|
|
823
|
+
function questionInlineKeyboard(shortHash, questionIndex, question, selected) {
|
|
824
|
+
const multiple = question.multiple === true;
|
|
825
|
+
const inlineKeyboard = question.options.map((option, optionIndex) => [{
|
|
826
|
+
text: multiple && selected.includes(option.label) ? `\u2705 ${option.label}` : option.label,
|
|
827
|
+
callback_data: buildCallbackData(shortHash, questionIndex, optionIndex)
|
|
828
|
+
}]);
|
|
829
|
+
if (question.custom !== false) {
|
|
830
|
+
inlineKeyboard.push([{ text: "\u270F\uFE0F Custom answer", callback_data: buildCallbackData(shortHash, questionIndex, "c") }]);
|
|
831
|
+
}
|
|
832
|
+
if (multiple) {
|
|
833
|
+
inlineKeyboard.push([{ text: "\u2705 Done", callback_data: buildCallbackData(shortHash, questionIndex, "d") }]);
|
|
834
|
+
}
|
|
835
|
+
return inlineKeyboard;
|
|
836
|
+
}
|
|
817
837
|
function questionPromptText(pending, questionIndex) {
|
|
818
838
|
const question = pending.questions[questionIndex];
|
|
819
839
|
const prefix = pending.questions.length > 1 ? `Question ${questionIndex + 1}/${pending.questions.length}
|
|
@@ -833,13 +853,7 @@ function answerSummary(questions, answers) {
|
|
|
833
853
|
async function editPromptForQuestion(ctx, pending, shortHash, questionIndex) {
|
|
834
854
|
const messageId = pending.telegramMessageIds[0];
|
|
835
855
|
const question = pending.questions[questionIndex];
|
|
836
|
-
const inlineKeyboard = question
|
|
837
|
-
text: option.label,
|
|
838
|
-
callback_data: buildCallbackData(shortHash, questionIndex, optionIndex)
|
|
839
|
-
}]);
|
|
840
|
-
if (question.custom !== false) {
|
|
841
|
-
inlineKeyboard.push([{ text: "\u270F\uFE0F Custom answer", callback_data: buildCallbackData(shortHash, questionIndex, "c") }]);
|
|
842
|
-
}
|
|
856
|
+
const inlineKeyboard = questionInlineKeyboard(shortHash, questionIndex, question, selectedAnswers(pending, questionIndex));
|
|
843
857
|
await ctx.bot.editMessageText(messageId, questionPromptText(pending, questionIndex), { reply_markup: { inline_keyboard: inlineKeyboard } });
|
|
844
858
|
}
|
|
845
859
|
async function completeIfReady(ctx, pending, shortHash) {
|
|
@@ -888,12 +902,9 @@ async function handleQuestionAsked(event, ctx) {
|
|
|
888
902
|
answersInProgress: request.questions.map(() => null)
|
|
889
903
|
};
|
|
890
904
|
try {
|
|
891
|
-
const message = request.questions.length === 1 ? await ctx.bot.sendQuestionWithKeyboard(firstQuestion, callbackDataForQuestion(shortHash, 0, firstQuestion)) : await ctx.bot.sendMessage(questionPromptText(pending, 0), {
|
|
905
|
+
const message = request.questions.length === 1 && useSimpleQuestionKeyboard(firstQuestion) ? await ctx.bot.sendQuestionWithKeyboard(firstQuestion, callbackDataForQuestion(shortHash, 0, firstQuestion)) : await ctx.bot.sendMessage(questionPromptText(pending, 0), {
|
|
892
906
|
reply_markup: {
|
|
893
|
-
inline_keyboard:
|
|
894
|
-
text: option.label,
|
|
895
|
-
callback_data: buildCallbackData(shortHash, 0, optionIndex)
|
|
896
|
-
}]).concat(firstQuestion.custom !== false ? [[{ text: "\u270F\uFE0F Custom answer", callback_data: buildCallbackData(shortHash, 0, "c") }]] : [])
|
|
907
|
+
inline_keyboard: questionInlineKeyboard(shortHash, 0, firstQuestion, [])
|
|
897
908
|
}
|
|
898
909
|
});
|
|
899
910
|
pending.telegramMessageIds = [message.message_id];
|
|
@@ -923,16 +934,32 @@ function createQuestionDispatcher(ctx) {
|
|
|
923
934
|
const question = pending.questions[questionIndex];
|
|
924
935
|
if (!question) return;
|
|
925
936
|
if (selection === "c") {
|
|
926
|
-
|
|
937
|
+
if (question.multiple === true) {
|
|
938
|
+
await ctx.bot.editMessageText(messageId, questionPromptText(pending, questionIndex), { reply_markup: { inline_keyboard: [] } });
|
|
939
|
+
} else {
|
|
940
|
+
await ctx.bot.editMessageRemoveKeyboard(messageId, "\u270F\uFE0F Reply to the next message with your custom answer.");
|
|
941
|
+
}
|
|
927
942
|
const prompt = await ctx.bot.replyWithForceReply("Type your custom answer", "Type your answer");
|
|
928
943
|
pending.awaitingCustomFor = { shortHash, questionIndex, chatId, userId, promptMessageId: prompt.message_id };
|
|
929
944
|
await ctx.pendingQuestions.savePending(shortHash, pending);
|
|
930
945
|
return;
|
|
931
946
|
}
|
|
947
|
+
if (selection === "d") {
|
|
948
|
+
if (question.multiple !== true) return;
|
|
949
|
+
pending.answersInProgress[questionIndex] = selectedAnswers(pending, questionIndex);
|
|
950
|
+
pending.awaitingCustomFor = void 0;
|
|
951
|
+
await completeIfReady(ctx, pending, shortHash);
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
932
954
|
const option = question.options[Number(selection)];
|
|
933
955
|
if (!option) return;
|
|
934
956
|
if (question.multiple === true) {
|
|
935
|
-
|
|
957
|
+
const current = selectedAnswers(pending, questionIndex);
|
|
958
|
+
pending.answersInProgress[questionIndex] = current.includes(option.label) ? current.filter((answer) => answer !== option.label) : [...current, option.label];
|
|
959
|
+
pending.awaitingCustomFor = void 0;
|
|
960
|
+
await ctx.pendingQuestions.savePending(shortHash, pending);
|
|
961
|
+
await editPromptForQuestion(ctx, pending, shortHash, questionIndex);
|
|
962
|
+
return;
|
|
936
963
|
}
|
|
937
964
|
pending.answersInProgress[questionIndex] = [option.label];
|
|
938
965
|
pending.awaitingCustomFor = void 0;
|
|
@@ -947,6 +974,16 @@ function createQuestionDispatcher(ctx) {
|
|
|
947
974
|
await expirePending(ctx, match.shortHash, match.data, match.data.telegramMessageIds[0]);
|
|
948
975
|
return;
|
|
949
976
|
}
|
|
977
|
+
const question = match.data.questions[awaiting.questionIndex];
|
|
978
|
+
if (question?.multiple === true) {
|
|
979
|
+
const current = selectedAnswers(match.data, awaiting.questionIndex);
|
|
980
|
+
match.data.answersInProgress[awaiting.questionIndex] = current.includes(text) ? current : [...current, text];
|
|
981
|
+
match.data.awaitingCustomFor = void 0;
|
|
982
|
+
await ctx.bot.sendMessage("\u2705 Custom answer added. Tap Done when finished.");
|
|
983
|
+
await ctx.pendingQuestions.savePending(match.shortHash, match.data);
|
|
984
|
+
await editPromptForQuestion(ctx, match.data, match.shortHash, awaiting.questionIndex);
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
950
987
|
match.data.answersInProgress[awaiting.questionIndex] = [text];
|
|
951
988
|
match.data.awaitingCustomFor = void 0;
|
|
952
989
|
await ctx.bot.sendMessage("\u2705 Custom answer sent.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coinseeker/opencode-telegram-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Control and monitor OpenCode from Telegram with notifications, question replies, and subagent-aware completion.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/telegram-remote.js",
|