Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
ai-bot-edge-function
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
duanjinfei
ai-bot-edge-function
Commits
c0e075cb
Commit
c0e075cb
authored
4 months ago
by
duanjinfei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bot conversation
parent
01fad00a
main
No related merge requests found
Changes
15
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
2386 additions
and
4 deletions
+2386
-4
context_ex.yaml
context_ex.yaml
+55
-0
.env
supabase/.env
+25
-1
cli-latest
supabase/.temp/cli-latest
+1
-1
index.ts
supabase/functions/check_bot_chat/index.ts
+109
-0
index.ts
supabase/functions/check_event/index.ts
+203
-0
index.ts
supabase/functions/fastgpt_webhook/index.ts
+44
-0
index.ts
supabase/functions/tg_bot_D/index.ts
+98
-2
index.ts
supabase/functions/tg_bot_E/index.ts
+141
-0
index.ts
supabase/functions/tg_bot_F/index.ts
+44
-0
bot.ts
supabase/functions/tg_bot_test/bot.ts
+129
-0
conversations.ts
supabase/functions/tg_bot_test/conversations.ts
+763
-0
conversations_util.ts
supabase/functions/tg_bot_test/conversations_util.ts
+189
-0
db.ts
supabase/functions/tg_bot_test/db.ts
+296
-0
fastgpt.ts
supabase/functions/tg_bot_test/fastgpt.ts
+228
-0
index.ts
supabase/functions/tg_bot_test/index.ts
+61
-0
No files found.
context_ex.yaml
0 → 100644
View file @
c0e075cb
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)
]
}
}
}
This diff is collapsed.
Click to expand it.
supabase/.env
View file @
c0e075cb
...
...
@@ -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
This diff is collapsed.
Click to expand it.
supabase/.temp/cli-latest
View file @
c0e075cb
v2.2.1
\ No newline at end of file
v2.6.8
\ No newline at end of file
This diff is collapsed.
Click to expand it.
supabase/functions/check_bot_chat/index.ts
0 → 100644
View file @
c0e075cb
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
This diff is collapsed.
Click to expand it.
supabase/functions/check_event/index.ts
0 → 100644
View file @
c0e075cb
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
This diff is collapsed.
Click to expand it.
supabase/functions/fastgpt_webhook/index.ts
0 → 100644
View file @
c0e075cb
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
This diff is collapsed.
Click to expand it.
supabase/functions/tg_bot_D/index.ts
View file @
c0e075cb
...
...
@@ -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
'
)
...
...
This diff is collapsed.
Click to expand it.
supabase/functions/tg_bot_E/index.ts
0 → 100644
View file @
c0e075cb
// 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
This diff is collapsed.
Click to expand it.
supabase/functions/tg_bot_F/index.ts
0 → 100644
View file @
c0e075cb
// 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
This diff is collapsed.
Click to expand it.
supabase/functions/tg_bot_test/bot.ts
0 → 100644
View file @
c0e075cb
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.
Click to expand it.
supabase/functions/tg_bot_test/conversations.ts
0 → 100644
View file @
c0e075cb
This diff is collapsed.
Click to expand it.
supabase/functions/tg_bot_test/conversations_util.ts
0 → 100644
View file @
c0e075cb
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.
Click to expand it.
supabase/functions/tg_bot_test/db.ts
0 → 100644
View file @
c0e075cb
This diff is collapsed.
Click to expand it.
supabase/functions/tg_bot_test/fastgpt.ts
0 → 100644
View file @
c0e075cb
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
This diff is collapsed.
Click to expand it.
supabase/functions/tg_bot_test/index.ts
0 → 100644
View file @
c0e075cb
// 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
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment