Commit c0e075cb authored by duanjinfei's avatar duanjinfei

bot conversation

parent 01fad00a
No related merge requests found
Context {
update: {
update_id: 379077430,
message: {
message_id: 558,
from: {
id: 5740112902,
is_bot: false,
first_name: "D",
last_name: "JJFF",
username: "DDDDJJJJ8888",
language_code: "zh-hans"
},
chat: {
id: 5740112902,
first_name: "D",
last_name: "JJFF",
username: "DDDDJJJJ8888",
type: "private"
},
date: 1736848472,
text: "/start",
entities: [ { offset: 0, length: 6, type: "bot_command" } ]
}
},
api: Api {
token: "7840941877:AAE7NJQvddWH-O00NLoaoQhIFQ1-tn2ihFY",
options: undefined,
raw: {},
config: {
use: [Function: use],
installedTransformers: [Function: installedTransformers]
}
},
me: {
id: 7840941877,
is_bot: true,
first_name: "chatflowBot",
username: "tg_chatflow_bot",
can_join_groups: true,
can_read_all_group_messages: false,
supports_inline_queries: false,
can_connect_to_business: false,
has_main_web_app: false
},
match: "",
session: [Getter/Setter],
conversation: ConversationControls {
enter: [AsyncFunction (anonymous)],
[Symbol(conversations)]: {
ids: Set(1) { "createagent" },
session: [AsyncFunction (anonymous)]
}
}
}
......@@ -2,11 +2,35 @@ TELEGRAM_BOT_TOKEN_A=6351960109:AAHi3c7_3-dxLSExSBxwZH6C-_v9pEQeIYE
TELEGRAM_BOT_TOKEN_B=7438818892:AAFOklft4GEyadDgq18Ise0eS53wtc2O9r8
TELEGRAM_BOT_TOKEN_C=7952562405:AAEB55ItdDQRPxg578JuOL-3EWm8KbqeXbc
TELEGRAM_BOT_TOKEN_D=7619523873:AAEzQ022F-n_qu_tGssJSpeDB0CZeMZXBrA
TELEGRAM_BOT_TOKEN_E=7403262814:AAEz5XTxYHbYNcRagazc6JdesSeDrB_Iq8Q
TELEGRAM_BOT_TOKEN_F=7844691549:AAExMlqa8G83u50ILZXs0Ns0lzexRhL9oY4
TELEGRAM_BOT_TOKEN_TEST=7840941877:AAE7NJQvddWH-O00NLoaoQhIFQ1-tn2ihFY
FUNCTION_SECRET_A=AAHi3c7_3-dxLSExSBxwZH6C-_v9pEQeIYE
FUNCTION_SECRET_B=AAFOklft4GEyadDgq18Ise0eS53wtc2O9r8
FUNCTION_SECRET_C=AAEB55ItdDQRPxg578JuOL-3EWm8KbqeXbc
FUNCTION_SECRET_D=AAEzQ022F-n_qu_tGssJSpeDB0CZeMZXBrA
FUNCTION_SECRET_E=AAEz5XTxYHbYNcRagazc6JdesSeDrB_Iq8Q
FUNCTION_SECRET_F=AAExMlqa8G83u50ILZXs0Ns0lzexRhL9oY4
FUNCTION_SECRET_TEST=AAE7NJQvddWH-O00NLoaoQhIFQ1-tn2ihFY
DIFY_API_KEY_A=app-hVXNz9r7ss5WlWCTphDBROxR
DIFY_API_KEY_B=app-K5m3nhVjNPYC5bjrz9jBedod
DIFY_API_KEY_C=app-3a1LeRzAG17TVPQo4qVYZWcH
DIFY_API_KEY_D=app-t3gQ1DIhBU8lK5bcTwdGLeGn
\ No newline at end of file
DIFY_API_KEY_D=app-t3gQ1DIhBU8lK5bcTwdGLeGn
DIFY_API_KEY_E=app-fzfERJzj2umyd0FYo48FBspa
DIFY_API_KEY_F=app-POOgCW7u2W9icnlFQnXB8UhJ
FASTGPT_API_KEY_D=fastgpt-wqtzzN6HY2FwDnTAChLlBa9vCZ0oOMXQe9ZqqBPaBhpoVyyDU1m7jAV
FASTGPT_API_URL=https://api.fastgpt.in/api/v1/chat/completions
FASTGPT_KNOWLEDGE_URL=https://api.fastgpt.in/api/core/dataset
FASTGPT_KNOWLEDGE_COLLECTION_URL=https://api.fastgpt.in/api/core/dataset/collection
FASTGPT_API_KEY_EVENT=fastgpt-awmDKXGxELXMaB9h9VRKjo3dQR7aUQ9etD4O8xeKzqFdafLyft3B2XsLq1Bm6LM
FASTGPT_API_KEY_AGENT=fastgpt-uHHPpoq0VtiZp5WB7XrkhxWNcIHhW0Fs4C0Ytm5zZAojNVHsOA0KaW9OAb7
FASTGPT_API_KEY_AGENT_CREATE=fastgpt-oYK4ghLDVUTHWfhzZrLL1avqW0lVyQR01W4Snp7fuMhn0UFEoL9Nvv
FASTGPT_API_KEY_ACTION=fastgpt-yHL5yVPhsB7OgTicXwje456cg6qsQeQKmFGQsGAeUqUZteYtaZFa
FASTGPT_API_KEY_ROLE=fastgpt-ssWwuvC1QFtX7rlckwycQP4x1uUMPRvzBZ9gpxCTSYPO1XqMqlk6Gq3P6Fw
FASTGPT_API_KEY_KNOWLEDGE=fastgpt-wwqDG9uVpDZemTYIlBQpqB9JHcFXSI9UDeYht0eVzRRKm5RGDZUjyE19QcM2
FASTGPT_API_KEY_ANSWER=fastgpt-yunKjnKn9YP1CKdm5yITZId1g49YUyMD77gaH2ExJEUZVshjjwQVf
AGENT_ADMIN=f06d467a-cd76-427e-a64b-e7a59a865848
SUPPORT_BOT=["tg_chat_flow","BotsHHHBot"]
SUPPORT_BOT_TOKEN=tg_chat_flow~7840941877:AAE7NJQvddWH-O00NLoaoQhIFQ1-tn2ihFY;BotsHHHBot~7844691549:AAExMlqa8G83u50ILZXs0Ns0lzexRhL9oY4
v2.2.1
\ No newline at end of file
v2.6.8
\ No newline at end of file
import { createClient } from "jsr:@supabase/supabase-js@2";
import { Bot } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
// 初始化 Supabase 客户端
const supabaseUrl = Deno.env.get("SUPABASE_URL");
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY");
const supabase = createClient(supabaseUrl, supabaseKey);
const sendTimeToleranceMs = 5 * 60 * 1000; // 5分钟的时间窗口
async function processBotChat() {
try {
const currentTime = new Date();
const startTime = new Date(currentTime.getTime() - sendTimeToleranceMs).toISOString();
const endTime = new Date(currentTime.getTime() + sendTimeToleranceMs).toISOString();
console.log(`Processing bot_chat records between ${startTime} and ${endTime}`);
// 查询符合条件的 bot_chat 记录
const { data: botChats, error: fetchError } = await supabase
.from("bot_chat")
.select("id, content, bot_id, agent_id, send_time")
.gte("send_time", startTime)
.lte("send_time", endTime)
.eq("status", "0");
if (fetchError) {
console.error("Error fetching bot_chat records:", fetchError);
return;
}
if (!botChats || botChats.length === 0) {
console.log("No bot_chat records found in the time window.");
return;
}
console.log(`Fetched ${botChats.length} bot_chat records.`);
// 处理每条 bot_chat 记录
for (const chat of botChats) {
const { id, content, bot_id, agent_id } = chat;
try {
// 查询 agent_tg 表获取 tg_chat_id
const { data: agentData, error: agentError } = await supabase
.from("agent_tg")
.select("tg_chat_id")
.eq("agent_id", agent_id)
if (agentError || !agentData || agentData.length === 0) {
console.error(`Failed to fetch tg_chat_id for agent_id ${agent_id}:`, agentError);
continue;
}
// 查询 agent_tg 表获取 tg_chat_id
const { data: botData, error: botError } = await supabase
.from("bot")
.select("id,tg_token")
.eq("id", bot_id)
.single();
if (botError || !botData) {
console.error(`Failed to fetch bot token for bot_id ${bot_id}:`, botError);
continue;
}
const { tg_chat_id } = agentData[0];
// 使用 bot_id 初始化 Telegram Bot
const bot = new Bot(botData.tg_token);
await bot.api.sendMessage(tg_chat_id, content);
console.log(`Message sent to chat_id ${tg_chat_id} for bot_chat ID ${id}`);
// 更新 bot_chat 状态为已发送
const { error: updateError } = await supabase
.from("bot_chat")
.update({ status: "1", finished_at: new Date().toISOString() })
.eq("id", id);
if (updateError) {
console.error(`Failed to update bot_chat status for ID ${id}:`, updateError);
}
} catch (err) {
console.error(`Error processing bot_chat ID ${id}:`, err);
}
}
} catch (err) {
console.error("Unexpected error in processBotChat:", err);
}
}
// 主流程:每次请求调用事件处理函数
Deno.serve(async () => {
try {
EdgeRuntime.waitUntil(processBotChat())
return new Response(JSON.stringify({ message: "Bot chat processed successfully" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (err) {
console.error("Unexpected error:", err);
return new Response(JSON.stringify({ error: "Internal server error" }), {
headers: { "Content-Type": "application/json" },
status: 500,
});
}
});
\ No newline at end of file
import { createClient } from "jsr:@supabase/supabase-js@2";
import { Cron } from "https://deno.land/x/croner@5.1.1/src/croner.js";
const AUTH_TOKEN = Deno.env.get('FASTGPT_API_KEY_EVENT');
const FASTGPT_API_URL = Deno.env.get('FASTGPT_API_URL');
interface ChatRequest {
stream: boolean;
detail: boolean;
variables: {
uid: string;
name: string;
};
messages: Array<{
role: string;
content: string;
}>;
}
// 初始化 Supabase 客户端
const supabaseUrl = Deno.env.get("SUPABASE_URL") ?? "";
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";
const supabase = createClient(supabaseUrl, supabaseKey);
const batchSize = 100;
const requestInterval = 300; // 每秒最多发送一个请求,控制 API 请求频率
// 查询并处理事件
async function processEventsInBatches() {
let offset = 0;
let hasMore = true;
// 获取当前时间
const currentTime = new Date().toISOString();
console.log(`Processing events at ${currentTime}`);
while (hasMore) {
// 查询符合条件的事件
const { data, error } = await supabase
.from("agent_event")
.select("id, trigger_cron, trigger_condition, event_type, is_triggered, triggered_count,last_triggered_time,send_cron")
.eq("is_triggered", false) // 只查询未触发的事件
.range(offset, offset + batchSize - 1); // 分页查询
if (error || !data) {
console.error("Error fetching events:", error);
break;
}
console.log(`Fetched ${data.length} events`);
// 处理当前批次的事件
await Promise.all(data.map(event => processEvent(event, currentTime)));
// 如果查询到的记录数少于批次大小,说明没有更多数据了
if (data.length < batchSize) {
hasMore = false;
}
// 更新 offset,继续查询下一批
offset += batchSize;
}
}
// 处理单个事件
async function processEvent(event: any, currentTime: string) {
console.log("Processing event:", event);
const { id, trigger_cron, send_cron, trigger_condition, event_type, last_triggered_time, triggered_count } = event;
const currentDateTime = new Date(currentTime);
const cron = new Cron(trigger_cron);
const sendCron = new Cron(send_cron);
const nextSendTime = sendCron.next(new Date());
trigger_condition.send_time = nextSendTime?.toISOString();
console.log("send_time:", trigger_condition.send_time);
// 使用 Cron 解析器获取下一次触发时间
const nextTriggerTime = cron.next(new Date(last_triggered_time));
// 容忍范围:1秒
const toleranceMs = 1000;
console.log(`Current time: ${currentDateTime}, Next trigger: ${nextTriggerTime}`);
if (
nextTriggerTime &&
currentDateTime.getTime() >= nextTriggerTime.getTime() - toleranceMs
) {
if (event_type === 1) {
console.log(`Processing recurrent event ${id}`);
await handleRecurrentEvent(trigger_condition, id, triggered_count);
} else if (event_type === 2) {
console.log(`Processing single event ${id}`);
await handleSingleEvent(trigger_condition, id, triggered_count);
}
// 更新 last_triggered_time
await updateLastTriggeredTime(id);
} else {
console.log(`Event ${id} not triggered. Current time: ${currentDateTime}, Next trigger: ${nextTriggerTime}`);
}
}
async function updateLastTriggeredTime(id: number) {
const { error } = await supabase
.from("agent_event")
.update({ last_triggered_time: new Date() })
.eq("id", id);
if (error) {
console.error(`Failed to update last_triggered_time for event ID ${id}:`, error);
}
}
// 处理循环任务
async function handleRecurrentEvent(triggerCondition: any, eventId: number, triggered_count: number) {
// 控制请求发送的频率
await triggerThirdPartyAPIWithRateLimit(triggerCondition);
// 更新事件的触发次数
const { error } = await supabase
.from("agent_event")
.update({ triggered_count: triggered_count + 1 })
.eq("id", eventId);
if (error) {
console.error("Failed to update triggered_count:", error);
}
}
// 处理单次任务
async function handleSingleEvent(triggerCondition: any, eventId: number, triggered_count: number) {
// 触发第三方 API
await triggerThirdPartyAPIWithRateLimit(triggerCondition);
// 标记事件为已触发
const { error } = await supabase
.from("agent_event")
.update({ is_triggered: true, triggered_count: triggered_count + 1 })
.eq("id", eventId);
if (error) {
console.error("Failed to mark event as triggered:", error);
}
}
// 向第三方 API 发送请求,并进行流控
let lastRequestTime = Date.now();
async function triggerThirdPartyAPIWithRateLimit(triggerCondition: any) {
const now = Date.now();
const timeSinceLastRequest = now - lastRequestTime;
// 等待直到达到时间间隔后再发送请求
if (timeSinceLastRequest < requestInterval) {
console.log("Waiting for next request...");
await new Promise(resolve => setTimeout(resolve, requestInterval - timeSinceLastRequest));
}
const requestData: ChatRequest = {
stream: false,
detail: false,
variables: triggerCondition,
messages: [
{
role: 'user',
content: ''
}
]
};
console.log("Sending request to third-party API:", requestData);
// 非阻塞地发送请求
fetch(FASTGPT_API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${AUTH_TOKEN}`
},
body: JSON.stringify(requestData),
}).catch((error) => {
console.error("API call failed:", error);
});
console.log("Request sent successfully");
// 更新最后请求时间
lastRequestTime = Date.now();
}
// 主流程:定时调用事件处理函数
Deno.serve(async (req) => {
try {
EdgeRuntime.waitUntil(processEventsInBatches())
return new Response(JSON.stringify({ message: "Events processed successfully" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (err) {
console.error('Unexpected error:', err);
return new Response(JSON.stringify({ error: 'Internal server error' }), {
headers: { 'Content-Type': 'application/json' },
status: 500,
});
}
});
\ No newline at end of file
import { createClient } from "jsr:@supabase/supabase-js@2";
import { Bot } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
// 初始化 Supabase 客户端
const supabaseUrl = Deno.env.get("SUPABASE_URL") ?? "";
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";
const supabase = createClient(supabaseUrl, supabaseKey);
async function processWebhook(req: any) {
const body = await req.json();
console.log('webhook body:', body);
const { tg_chat_id, bot_id, content } = body;
if (!tg_chat_id || !bot_id || !content) {
console.error('Invalid request body:', body);
return;
}
// 通过bot_id查询bot 获得tg_id
const { data: botsData, error } = await supabase.from("bot").select("tg_token").eq("id", bot_id).single();
if (error || !botsData) {
console.error("获取Bot数据失败:", error?.message || "未知错误");
return;
}
const bot = new Bot(botsData.tg_token);
console.log('bot:', bot);
await bot.api.sendMessage(tg_chat_id, content);
console.log('Message sent successfully');
}
// 主流程:定时调用事件处理函数
Deno.serve(async (req) => {
try {
EdgeRuntime.waitUntil(processWebhook(req))
return new Response(JSON.stringify({ message: "Events processed successfully" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (err) {
console.error('Unexpected error:', err);
return new Response(JSON.stringify({ error: 'Internal server error' }), {
headers: { 'Content-Type': 'application/json' },
status: 500,
});
}
});
\ No newline at end of file
......@@ -6,13 +6,109 @@ console.log(`Function "telegram-bot" up and running!`)
import { Bot, webhookCallback } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
const API_URL = 'https://api.fastgpt.in/api/v1/chat/completions';
interface ChatRequest {
chatId: string;
stream: boolean;
detail: boolean;
responseChatItemId: string;
variables: {
uid: string;
name: string;
};
messages: Array<{
role: string;
content: string;
}>;
}
interface ChatResponse {
id: string;
model: string;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
choices: Array<{
message: {
role: string;
content: string;
};
finish_reason: string;
index: number;
}>;
}
async function sendChatRequest(requestData: ChatRequest): Promise<ChatResponse | null> {
try {
const AUTH_TOKEN = Deno.env.get('FASTGPT_API_KEY_D');
console.log("AUTH_TOKEN:", AUTH_TOKEN);
const response = await fetch(API_URL, {
method: 'POST',
headers: {
Authorization: `Bearer ${AUTH_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData),
});
if (!response.ok) {
console.error('Failed to fetch:', response.statusText);
return null;
}
const data: ChatResponse = await response.json();
return data;
} catch (error) {
console.error('Error during request:', error);
return null;
}
}
const bot = new Bot(Deno.env.get('TELEGRAM_BOT_TOKEN_D') || '')
bot.command('start', (ctx) => ctx.reply('Welcome! Up and running.'))
bot.command('start', (ctx) => {
console.log("ctx:", ctx.message.text);
ctx.reply('Welcome! Up and running.')
})
bot.command('ping', (ctx) => ctx.reply(`Pong! ${new Date()} ${Date.now()}`))
// bot.on('message', (ctx) => ctx.reply('你好,我是tg_D'))
bot.on('message:text', (ctx) => respMsg(ctx))
async function respMsg(ctx) {
const message = ctx.message as Message.TextMessage;
let question = message.text;
const requestData: ChatRequest = {
chatId: 'my_chat_id_20',
stream: false,
detail: false,
// responseChatItemId: 'my_responseChatItemId_1',
variables: {
selectdata: [
// { datasetId: "677df2c0fd2ccc1d37088969" },
// { datasetId: "677e13e29c52479ad3fce8b8" },
// { datasetId: "677f79639c52479ad308090f" },
{ datasetId: "677e27e9fd2ccc1d370b6737" }
]
},
messages: [
{
role: 'user',
content: question,
},
],
};
let respMsg = await sendChatRequest(requestData);
if (respMsg) {
console.log("respMsg:", respMsg);
await bot.api.sendMessage(ctx.chat.id, respMsg.choices[0].message.content);
}
}
const handleUpdate = webhookCallback(bot, 'std/http')
......
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
console.log(`Function "telegram-bot" up and running!`)
import { Bot, webhookCallback, Context } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
interface DifyResponse {
answer?: string;
[key: string]: any;
}
interface DifyPayload {
inputs: Record<string, any>;
query: string;
response_mode: string;
conversation_id: string;
user: string;
files: any[];
}
let question = "";
let answer = "";
const bot = new Bot(Deno.env.get('TELEGRAM_BOT_TOKEN_E') || '')
const bot_b = new Bot(Deno.env.get('TELEGRAM_BOT_TOKEN_F') || '')
bot.command('start', (ctx) => ctx.reply('Welcome! Up and running.'))
bot.command('ping', (ctx) => ctx.reply(`Pong! ${new Date()} ${Date.now()}`))
bot.on('message', (ctx) => {
// console.log("ctx:", ctx);
// ctx.reply('你好,我是tg_E');
handleMessage(ctx, true);
})
const handleUpdate = webhookCallback(bot, 'std/http')
async function callDifyApi(
query: string,
apiKey: string,
conversationId: string = ""
): Promise<DifyResponse> {
const url = "https://api.dify.ai/v1/chat-messages";
const headers = {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
};
const payload: DifyPayload = {
inputs: {},
query: query,
response_mode: "blocking",
conversation_id: conversationId,
user: "telegram-user",
files: []
};
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload),
});
try {
const jsonData = await response.json();
return jsonData; // 返回解析后的 JSON 数据
} catch (err) {
const text = await response.text(); // 捕获非 JSON 响应
console.error("Response is not JSON:", text);
let res = { answer: text };
return res;
}
}
async function callDify(query: string, difyApiKey: string): Promise<string> {
const response = await callDifyApi(query, difyApiKey);
console.log(difyApiKey, response);
return response.answer || "Sorry, I don't understand.";
}
async function handleMessage_userToE(chatId: number, userMessage: string) {
console.log("handleMessage_userToE", userMessage);
const difyResponse = await callDify(userMessage, Deno.env.get('DIFY_API_KEY_E') || "");
await bot.api.sendMessage(chatId, difyResponse);
return difyResponse;
}
async function handleMessage_E_send_F(chatId: number, userMessage: string) {
console.log("handleMessage_E_send_F", userMessage);
const difyResponse = await callDify(userMessage, Deno.env.get('DIFY_API_KEY_F') || "");
await bot_b.api.sendMessage(chatId, difyResponse);
return difyResponse;
}
async function handleMessage(ctx: Context, isFirst: boolean) {
let i = 0;
while (i < 5) {
i++;
if (isFirst) {
const message = ctx.message as Message.TextMessage;
question = message.text;
isFirst = false;
}
answer = await handleMessage_userToE(ctx.chat.id, question);
await new Promise(resolve => setTimeout(resolve, 2000)); // 延时2秒钟
question = await handleMessage_E_send_F(ctx.chat.id, answer) || "";
console.log("question", question);
}
}
Deno.serve(async (req: Request) => {
try {
const url = new URL(req.url)
if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET_E')) {
return new Response('not allowed', { status: 405 })
}
// 检查请求体
if (req.body === null) {
return new Response('Empty request body', { status: 400 })
}
const response = await handleUpdate(req)
if (!response) {
return new Response('Invalid update', { status: 400 })
}
return response
} catch (err) {
console.error(`Error processing request ${req.method} ${req.url}:`, err)
return new Response(JSON.stringify({ error: err.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
})
\ No newline at end of file
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
console.log(`Function "telegram-bot" up and running!`)
import { Bot, webhookCallback } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
const bot = new Bot(Deno.env.get('TELEGRAM_BOT_TOKEN_F') || '')
bot.command('start', (ctx) => ctx.reply('Welcome! Up and running.'))
bot.command('ping', (ctx) => ctx.reply(`Pong! ${new Date()} ${Date.now()}`))
// bot.on('message', (ctx) => ctx.reply('你好,我是tg_F'))
const handleUpdate = webhookCallback(bot, 'std/http')
Deno.serve(async (req) => {
try {
const url = new URL(req.url)
if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET_F')) {
return new Response('not allowed', { status: 405 })
}
// 检查请求体
if (req.body === null) {
return new Response('Empty request body', { status: 400 })
}
const response = await handleUpdate(req)
if (!response) {
return new Response('Invalid update', { status: 400 })
}
return response
} catch (err) {
console.error(err)
return new Response(JSON.stringify({ error: err.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
})
\ No newline at end of file
import { Bot, Context, SessionFlavor } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
import { type ConversationFlavor, conversations, createConversation } from "https://raw.githubusercontent.com/grammyjs/conversations/c90b7fed54d610b7a37c2e0b90e728bc4d739d9b/src/mod.ts";
import { guard, isAdmin, reply } from "https://deno.land/x/grammy_guard/mod.ts";
import { freeStorage } from "https://deno.land/x/grammy_storages@v2.4.2/free/src/mod.ts";
import {
createagent, updateagent, deleteagent,
createrole, updaterole, listroles, deleterole,
listagents, bindchatagent, answerQuestion,
createknowleage, listknowledges, addknowlegecollection,
listknowlegecollection, deleteknowlegecollection,
createaction
} from "./conversations.ts";
// Define a guard
const isAdminGuard = guard(isAdmin, reply("You are not an admin"));
// Define a context flavor
interface SessionData { agent: { agentName: string; agentDescription: string; } }
type MyContext = Context & SessionFlavor<SessionData> & ConversationFlavor<Context>;
async function constructorBot(botToken: string) {
const bot = new Bot<MyContext>(botToken)
let storageAdapter = freeStorage<SessionData>(botToken);
const storage = {
storage: {
type: "key",
adapter: storageAdapter,
getStorageKey: (ctx) => ctx.from?.id.toString(),
prefix: "agent-",
},
}
bot.use(conversations(storage));
bot.use(createConversation(bindchatagent));
bot.use(createConversation(createaction));
bot.use(createConversation(createagent));
bot.use(createConversation(updateagent));
bot.use(createConversation(deleteagent));
bot.use(createConversation(listagents));
bot.use(createConversation(createrole));
bot.use(createConversation(updaterole));
bot.use(createConversation(listroles));
bot.use(createConversation(deleterole));
bot.use(createConversation(createknowleage));
bot.use(createConversation(listknowledges));
bot.use(createConversation(addknowlegecollection));
bot.use(createConversation(listknowlegecollection));
bot.use(createConversation(deleteknowlegecollection));
// Define commands
bot.command('start', async (ctx) => {
await ctx.reply(`Welcome! Up and running.`)
})
bot.command("cancel", isAdminGuard, async (ctx) => {
if (ctx.conversation.active()) {
await ctx.conversation.exitAll();
}
await ctx.reply("Leaving.");
});
bot.command("bindchatagent", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("bindchatagent");
});
bot.command("createaction", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("createaction");
});
// Agent
bot.command("createagent", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("createagent");
});
bot.command("updateagent", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("updateagent");
});
bot.command("deleteagent", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("deleteagent");
});
bot.command("listagents", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("listagents");
});
// Knowledge
bot.command("createknowleage", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("createknowleage");
});
bot.command("listknowledges", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("listknowledges");
});
// Knowledge Collection
bot.command("addknowlegecollection", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("addknowlegecollection");
});
bot.command("listknowlegecollection", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("listknowlegecollection");
});
bot.command("deleteknowlegecollection", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("deleteknowlegecollection");
});
// Role
bot.command("createrole", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("createrole");
});
bot.command("updaterole", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("updaterole");
});
bot.command("listroles", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("listroles");
});
bot.command("deleterole", isAdminGuard, async (ctx) => {
await ctx.conversation.enter("deleterole");
});
bot.on("message:text", async (ctx) => {
console.log("ctx.message.text:", ctx.message.text);
console.log("ctx.me.username:", ctx.me.username);
const mentions = ctx.message.text.match(/@\w+/g);
if (mentions?.includes(`@${ctx.me.username}`)) {
const cleanText = ctx.message.text.replace(/@\w+/g, "").trim();
ctx.message.text = cleanText
EdgeRuntime.waitUntil(answerQuestion(ctx));
}
});
return bot;
}
export { constructorBot }
\ No newline at end of file
This diff is collapsed.
import { InlineKeyboard } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
// 工具函数:等待用户输入文本
async function waitForTextInput(
conversation: MyConversation,
ctx: MyContext,
promptMessage: string
): Promise<string | null> {
await ctx.reply(promptMessage);
const inputCtx = await conversation.waitFor(":text");
console.log("inputCtx.message.text:", inputCtx.message.text);
if (inputCtx.message.text.startsWith("/skip")) {
return "/skip";
}
if (inputCtx.message.text.startsWith("/cancel")) {
await ctx.reply("操作已取消");
return null;
}
return inputCtx.message.text;
}
async function waitForTextInputTips(
conversation: MyConversation,
ctx: MyContext,
promptMessage: string
): Promise<string | null> {
// reply 格式化提示信息,让信息更加友好
promptMessage = promptMessage.replace(/<b>/g, "[b]").replace(/<\/b>/g, "[/b]").replace(/<i>/g, "[i]").replace(/<\/i>/g, "[/i]");
await ctx.reply(promptMessage);
const inputCtx = await conversation.waitFor(":text");
console.log("inputCtx.message.text:", inputCtx.message.text);
if (inputCtx.message.text.startsWith("/skip")) {
return null;
}
if (inputCtx.message.text.startsWith("/cancel")) {
await ctx.reply("操作已取消");
return null;
}
return inputCtx.message.text;
}
async function waitForFileInput(
conversation: MyConversation,
ctx: MyContext,
promptMessage: string
): Promise<string | null> {
await ctx.reply(promptMessage);
const inputCtx = await conversation.waitFor(":file");
console.log("inputCtx", inputCtx);
const res = await inputCtx.getFile();
console.log("res", res);
return inputCtx.message.text;
}
// 工具函数:等待用户从键盘选择选项
async function waitForOptionSelection(
conversation: MyConversation,
ctx: MyContext,
promptMessage: string,
options: Map<string, string>,
isAddSkip: boolean
): Promise<{ id: string; name: string } | null> {
if (options.size === 0) {
await ctx.reply("没有可用的选项");
return null;
}
const keyboard = new InlineKeyboard();
for (const [id, name] of options.entries()) {
keyboard.text(name, id);
keyboard.row();
}
if (isAddSkip) {
options.set("skip", "skip");
keyboard.row().text("skip", "skip");
}
const sentMessage = await ctx.reply(promptMessage, { reply_markup: keyboard });
const response = await conversation.waitForCallbackQuery([...options.keys()], { timeout: 60_000 });
const selectedId = response.match;
if (selectedId == "skip") {
await response.answerCallbackQuery(); // 反馈用户操作
await response.api.editMessageReplyMarkup(sentMessage.chat.id, sentMessage.message_id, {
reply_markup: undefined, // 清除按钮
});
return { id: "skip", name: "skip" };
}
const selectedName = options.get(selectedId);
console.log("selectedName:", selectedName);
if (selectedName) {
// 编辑消息,移除 Inline Keyboard 或标记为已选择
await response.api.editMessageReplyMarkup(sentMessage.chat.id, sentMessage.message_id, {
reply_markup: undefined, // 清除按钮
});
// 可选:更新消息文本,标明用户选择
await response.api.editMessageText(sentMessage.chat.id, sentMessage.message_id,
`You choosed:${selectedName}`);
}
await response.answerCallbackQuery(); // 反馈用户操作
return selectedName ? { id: selectedId, name: selectedName } : null;
}
async function waitForMoreOptionSelection(
conversation: MyConversation,
ctx: MyContext,
promptMessage: string,
options: Map<string, string>,
isAddSkip: Boolean
): Promise<Array<{ id: string; name: string }>> {
if (options.size === 0) {
await ctx.reply("没有可用的选项");
return [];
}
let selectedOptions = new Set<string>();
// 构建键盘
const getKeyboard = () => {
const keyboard = new InlineKeyboard();
for (const [id, name] of options.entries()) {
const label = selectedOptions.has(id) ? `✅ ${name}` : name;
keyboard.text(label, id);
}
options.set("done", "done");
if (isAddSkip) {
options.set("skip", "skip");
keyboard.row().text("skip", "skip").text("done", "done");
} else {
keyboard.row().text("done", "done")
}
return keyboard;
};
// 发送消息
let sentMessage = await ctx.reply(promptMessage, { reply_markup: getKeyboard() });
while (true) {
const response = await conversation.waitForCallbackQuery([...options.keys()], { timeout: 60000 });
const selectedId = response.match;
if (selectedId == "skip") {
await response.answerCallbackQuery({ text: "seleted done!" });
// 将所有的选项都隐藏,
await response.api.editMessageReplyMarkup(sentMessage.chat.id, sentMessage.message_id, {
reply_markup: undefined,
});
break;
}
// if user click "done"
if (selectedId === "done") {
await response.answerCallbackQuery({ text: "seleted done!" });
// 将所有的选项都隐藏,
await response.api.editMessageReplyMarkup(sentMessage.chat.id, sentMessage.message_id, {
reply_markup: undefined,
});
const selectedLanguages = Array.from(selectedOptions).map((id) => ({
id,
name: options.get(id)!,
}));
await response.api.editMessageText(sentMessage.chat.id, sentMessage.message_id,
`You selected:${selectedLanguages.map((lang) => lang.name).join(", ")}`);
break;
}
// 切换选中状态
if (selectedOptions.has(selectedId)) {
selectedOptions.delete(selectedId);
} else {
selectedOptions.add(selectedId);
}
// 更新键盘显示状态
await response.answerCallbackQuery(); // 反馈用户操作
await response.api.editMessageReplyMarkup(sentMessage.chat.id, sentMessage.message_id, {
reply_markup: getKeyboard(),
});
}
// 返回结果
const result = Array.from(selectedOptions).map((id) => ({
id,
name: options.get(id)!,
}));
return result;
}
export { waitForTextInput, waitForOptionSelection, waitForMoreOptionSelection, waitForTextInputTips, waitForFileInput };
\ No newline at end of file
This diff is collapsed.
interface ChatRequest {
chatId: string;
stream: boolean;
detail: boolean;
responseChatItemId: string;
variables: {};
messages: Array<{
role: string;
content: string;
}>;
}
interface ChatResponse {
id: string;
model: string;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
choices: Array<{
message: {
role: string;
content: string;
};
finish_reason: string;
index: number;
}>;
}
interface CreateKnowledgeRequest {
name: string;
}
interface CreateKnowledgeCollectionRequest {
link: string;
datasetId: string;
name: string;
trainingType: string;
}
interface DeleteKnowledgeCollectionRequest {
id: string;
}
interface CreateKnowledgeCollectionResponse {
code: number;
statusText: string;
message: string;
data: {
collectionId: string;
};
}
interface CreateKnowledgeResponse {
code: number;
statusText: string;
message: string;
data: string;
}
async function sendChatRequest(requestData: ChatRequest, auth_token: string): Promise<ChatResponse | null> {
try {
const API_URL = Deno.env.get('FASTGPT_API_URL') ?? "";
const response = await fetch(API_URL, {
method: 'POST',
headers: {
Authorization: `Bearer ${auth_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData),
});
if (!response.ok) {
console.error('Failed to fetch:', response.statusText);
return null;
}
const data: ChatResponse = await response.json();
return data;
} catch (error) {
console.error('Error during request:', error);
return null;
}
}
async function postKnowledgeRequest(requestData: CreateKnowledgeRequest, auth_token: string, op: string): Promise<CreateKnowledgeResponse | null> {
try {
const API_URL = Deno.env.get('FASTGPT_KNOWLEDGE_URL') ?? "";
const response = await fetch(`${API_URL}/${op}`, {
method: 'POST',
headers: {
Authorization: `Bearer ${auth_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData),
});
if (!response.ok) {
console.error('Failed to fetch:', response.statusText);
return null;
}
const data: CreateKnowledgeResponse = await response.json();
if (data.code != 200) {
console.error('Failed to fetch:', data.message);
return null;
}
return data;
} catch (error) {
console.error('Error during request:', error);
return null;
}
}
async function postKnowledgeCollectionRequest(requestData, auth_token: string, is_create: boolean, op: string): Promise<CreateKnowledgeCollectionResponse | null> {
try {
const API_URL = Deno.env.get('FASTGPT_KNOWLEDGE_COLLECTION_URL') ?? "";
let url = `${API_URL}${is_create ? "/create" : "/delete"}${op == "" ? "" : op}`
const method = `${is_create ? "POST" : "DELETE"}`
const request = {
method: method,
headers: {
Authorization: `Bearer ${auth_token}`,
'Content-Type': 'application/json',
}
}
if (is_create) {
request.body = JSON.stringify(requestData);
} else {
url = `${url}?id=${requestData.id}`
}
console.log("method:", method);
console.log("url:", url);
console.log("request:", request);
const response = await fetch(url, request);
if (!response.ok) {
console.error('Failed to fetch:', response.statusText);
return null;
}
const data: CreateKnowledgeCollectionResponse = await response.json();
if (data.code != 200) {
console.error('Failed to fetch:', data.message);
return null;
}
return data;
} catch (error) {
console.error('Error during request:', error);
return null;
}
}
async function respMsgAgent(variables, question: any, auth_token: string) {
const requestData: ChatRequest = {
stream: false,
detail: false,
variables: variables,
messages: [
{
role: 'user',
content: question,
},
],
};
console.log("Sending request to third-party API:", requestData);
const res = await sendChatRequest(requestData, auth_token);
console.log("Send request to third-party API success");
if (res == null) {
return { data: null, error: new Error("Failed to fetch response") };
}
return { data: res, error: null };
}
async function respKnowledgeApi(name: string, auth_token: string) {
const requestData = {
name: name
};
const res = await postKnowledgeRequest(requestData, auth_token, "create");
if (res == null) {
return { data: null, error: new Error("Failed to fetch response") };
}
return { data: res, error: null };
}
async function respKnowledgeCollectionApi(requestData: CreateKnowledgeCollectionRequest, auth_token: string) {
const res = await postKnowledgeCollectionRequest(requestData, auth_token, true, "/link");
if (res == null) {
return { data: null, error: new Error("Failed to fetch response") };
}
return { data: res, error: null };
}
async function respKnowledgeCollectionDelApi(requestData: DeleteKnowledgeCollectionRequest, auth_token: string) {
const res = await postKnowledgeCollectionRequest(requestData, auth_token, false, "");
if (res == null) {
return { data: null, error: new Error("Failed to fetch response") };
}
return { data: res, error: null };
}
async function respMsgQuestion(variables, question: any, auth_token: string) {
const requestData: ChatRequest = {
stream: false,
detail: false,
variables: variables,
messages: [
{
role: 'user',
content: question,
},
],
};
console.log("Sending request to third-party API:", requestData);
const res = await sendChatRequest(requestData, auth_token);
console.log("Send request to third-party API success");
if (res == null) {
return { data: null, error: new Error("Failed to fetch response") };
}
return { data: res, error: null };
}
export {
respMsgAgent, respMsgQuestion, respKnowledgeApi,
respKnowledgeCollectionApi, respKnowledgeCollectionDelApi
}
\ No newline at end of file
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
console.log(`Function "telegram-bot" up and running!`)
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { webhookCallback } from 'https://deno.land/x/grammy@v1.34.0/mod.ts'
import { constructorBot } from "./bot.ts";
Deno.serve(async (req) => {
try {
const url = new URL(req.url)
if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET_TEST')) {
return new Response('not allowed', { status: 405 })
}
const user = url.searchParams.get('user');
const supportBot = JSON.parse(Deno.env.get('SUPPORT_BOT') || '[]');
if (!supportBot.includes(user)) {
return new Response('not allowed', { status: 405 })
}
// 检查请求体
if (req.body === null) {
return new Response('Empty request body', { status: 400 })
}
const SUPPORT_BOT_TOKEN_ENV = Deno.env.get('SUPPORT_BOT_TOKEN') || '';
const supportBotToken = SUPPORT_BOT_TOKEN_ENV.split(";").reduce((acc, pair) => {
const [key, value] = pair.split("~");
acc[key] = value;
return acc;
}, {});
console.log("supportBotToken:", supportBotToken);
const botToken = supportBotToken[user];
console.log("botToken:", botToken);
const bot = await constructorBot(botToken);
const handleUpdate = webhookCallback(bot, 'std/http');
const response = await handleUpdate(req)
if (!response) {
return new Response('Invalid update', { status: 400 })
}
return response
} catch (err) {
console.error(err)
return new Response(JSON.stringify({ error: err.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
})
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment