halbot 1995.1.42 → 1995.1.43
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/inspect_turn.mjs +28 -0
- package/lib/hal.mjs +5 -0
- package/package.json +1 -1
- package/pipeline/080_history.mjs +2 -2
- package/pipeline/100_chat.mjs +2 -2
- package/web/turn.html +102 -52
- package/web/turn.mjs +88 -9
package/inspect_turn.mjs
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
import { dbio, hal, storage as _storage } from './index.mjs';
|
|
3
|
+
|
|
4
|
+
const run = async () => {
|
|
5
|
+
const { config } = await _storage.getConfig();
|
|
6
|
+
await dbio.init(config.storage);
|
|
7
|
+
const token = 'TOKEN|8df8db54-3ef1-4d63-b91d-da05ae6eccff-d46ae000661b3509e743d37d55a7e30be8158c008ad93cad88d7af07503e2061c53bce89f042762495d168af02f';
|
|
8
|
+
const result = await dbio.queryOne(
|
|
9
|
+
`SELECT * FROM ${hal.table} WHERE token = $1`,
|
|
10
|
+
[token]
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
if (result) {
|
|
14
|
+
console.log('Result found.');
|
|
15
|
+
const response = JSON.parse(result.response);
|
|
16
|
+
console.log('Response count:', response.length);
|
|
17
|
+
|
|
18
|
+
response.forEach((msg, i) => {
|
|
19
|
+
console.log(`\nMessage ${i}:`);
|
|
20
|
+
console.log(JSON.stringify(msg, null, 2));
|
|
21
|
+
});
|
|
22
|
+
} else {
|
|
23
|
+
console.log('No result found for token.');
|
|
24
|
+
}
|
|
25
|
+
process.exit(0);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
run().catch(console.error);
|
package/lib/hal.mjs
CHANGED
|
@@ -168,6 +168,7 @@ const init = async (options) => {
|
|
|
168
168
|
controllerPath: options?.web?.controllerPath || getPath('../web'),
|
|
169
169
|
cfTunnel: options?.web?.cfTunnel,
|
|
170
170
|
});
|
|
171
|
+
const url = options?.web?.url || web?.listen;
|
|
171
172
|
if (callosum.isPrimary) {
|
|
172
173
|
const { ais } = await alan.initChat({ sessions: null });
|
|
173
174
|
const cmds = options?.cmds || [];
|
|
@@ -178,6 +179,10 @@ const init = async (options) => {
|
|
|
178
179
|
return x.model;
|
|
179
180
|
}).map(x => x.supportedMimeTypes || []).flat().map(x => x.toLowerCase()));
|
|
180
181
|
const _bot = await bot.init(options);
|
|
182
|
+
callosum.register(
|
|
183
|
+
'getFileLink', async id => await _bot.telegram.getFileLink(id)
|
|
184
|
+
);
|
|
185
|
+
callosum.register('getUrl', async () => url);
|
|
181
186
|
const pkg = await utilitas.which();
|
|
182
187
|
_ = {
|
|
183
188
|
args: { ...options?.args || {} },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "halbot",
|
|
3
3
|
"description": "Just another AI powered Telegram bot, which is simple design, easy to use, extendable and fun.",
|
|
4
|
-
"version": "1995.1.
|
|
4
|
+
"version": "1995.1.43",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/Leask/halbot",
|
|
7
7
|
"type": "module",
|
package/pipeline/080_history.mjs
CHANGED
|
@@ -105,9 +105,9 @@ const memorize = async (ctx) => {
|
|
|
105
105
|
const received_text = ctx._.request || ctx._.text || '';
|
|
106
106
|
const id = received.update_id;
|
|
107
107
|
let response = {};
|
|
108
|
-
ctx._.done.map(m =>
|
|
108
|
+
ctx._.done.map(m => response[m.message_id] = m);
|
|
109
109
|
response = Object.values(response).sort((a, b) => a.message_id - b.message_id);
|
|
110
|
-
const response_text = ctx?._
|
|
110
|
+
const response_text = ctx?._?.response || response.map(x => x?.text || '').filter(Boolean).join('\n');
|
|
111
111
|
const collected = ctx._.collected.filter(x => String.isString(x.content));
|
|
112
112
|
const distilled = compact(bot.lines([
|
|
113
113
|
received_text, response_text, ...collected.map(x => x.content)
|
package/pipeline/100_chat.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { alan } from '../index.mjs';
|
|
1
|
+
import { callosum, alan } from '../index.mjs';
|
|
2
2
|
import { token } from 'webjam';
|
|
3
3
|
|
|
4
4
|
const _name = 'Chat';
|
|
@@ -92,7 +92,7 @@ const action = async (ctx, next) => {
|
|
|
92
92
|
// 11: How are image, audio, and video formats displayed? (File tokens must be protected and require server-side forwarding.)
|
|
93
93
|
await ctx.edit(lastMsg.message_id,
|
|
94
94
|
lastMsg.raw
|
|
95
|
-
+ `\n\n\-\-\-\n\n✨ [View
|
|
95
|
+
+ `\n\n\-\-\-\n\n✨ [View web version](${await callosum.call('getUrl')}/turns/${ctx._.token}).`
|
|
96
96
|
);
|
|
97
97
|
await next();
|
|
98
98
|
};
|
package/web/turn.html
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>HAL9000 Chat</title>
|
|
8
8
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
9
|
+
<link rel="stylesheet"
|
|
10
|
+
href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.6.1/github-markdown-light.min.css">
|
|
11
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
|
|
12
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
9
13
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
14
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
15
|
<link
|
|
@@ -52,8 +56,7 @@
|
|
|
52
56
|
background-color: transparent;
|
|
53
57
|
color: var(--text-color);
|
|
54
58
|
font-family: var(--font-body);
|
|
55
|
-
height: 100%;
|
|
56
|
-
overflow: hidden;
|
|
59
|
+
min-height: 100%;
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
|
|
@@ -133,23 +136,11 @@
|
|
|
133
136
|
|
|
134
137
|
/* Main Content */
|
|
135
138
|
main {
|
|
136
|
-
height: 100vh;
|
|
137
|
-
/* Fallback */
|
|
138
|
-
height: 100dvh;
|
|
139
|
-
/* Mobile viewport fix */
|
|
140
139
|
padding-top: calc(70px + 1rem);
|
|
141
|
-
/* Header height + Gap (same as gap between bubbles) */
|
|
142
140
|
padding-bottom: 80px;
|
|
143
|
-
/* Footer height + gap */
|
|
144
141
|
padding-left: 20px;
|
|
145
142
|
padding-right: 20px;
|
|
146
143
|
box-sizing: border-box;
|
|
147
|
-
overflow-y: auto;
|
|
148
|
-
-webkit-overflow-scrolling: touch;
|
|
149
|
-
/* iOS Momentum Scrolling */
|
|
150
|
-
scroll-behavior: smooth;
|
|
151
|
-
overscroll-behavior-y: contain;
|
|
152
|
-
/* Prevent pull-to-refresh on body */
|
|
153
144
|
max-width: 900px;
|
|
154
145
|
margin: 0 auto;
|
|
155
146
|
width: 100%;
|
|
@@ -241,39 +232,64 @@
|
|
|
241
232
|
}
|
|
242
233
|
|
|
243
234
|
/* Markdown Styles */
|
|
244
|
-
|
|
245
|
-
.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
font-
|
|
249
|
-
|
|
235
|
+
/* Markdown Overrides */
|
|
236
|
+
.markdown-body {
|
|
237
|
+
background-color: transparent !important;
|
|
238
|
+
font-family: inherit !important;
|
|
239
|
+
font-size: 1em !important;
|
|
240
|
+
padding: 16px !important;
|
|
250
241
|
}
|
|
251
242
|
|
|
252
|
-
.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
font-family: 'Courier New', Courier, monospace;
|
|
258
|
-
font-size: 0.9em;
|
|
259
|
-
color: #d63384;
|
|
260
|
-
/* Pinkish code color */
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
.message-bubble pre {
|
|
264
|
-
background: #f4f4f4;
|
|
265
|
-
/* Light background */
|
|
266
|
-
padding: 1rem;
|
|
267
|
-
border-radius: 4px;
|
|
268
|
-
overflow-x: auto;
|
|
269
|
-
border: 1px solid #ddd;
|
|
270
|
-
color: #333;
|
|
243
|
+
.markdown-body img {
|
|
244
|
+
max-width: 100%;
|
|
245
|
+
border-radius: 8px;
|
|
246
|
+
display: block;
|
|
247
|
+
margin: 10px 0;
|
|
271
248
|
}
|
|
272
249
|
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
250
|
+
.markdown-body> :first-child {
|
|
251
|
+
margin-top: 0 !important;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.markdown-body> :last-child {
|
|
255
|
+
margin-bottom: 0 !important;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
/* Code Block Fixes */
|
|
261
|
+
.markdown-body pre {
|
|
262
|
+
padding: 36px 16px 16px 16px !important;
|
|
263
|
+
background-color: #f6f8fa !important;
|
|
264
|
+
border-radius: 6px !important;
|
|
265
|
+
position: relative !important;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.markdown-body pre[data-language]::before {
|
|
269
|
+
content: attr(data-language);
|
|
270
|
+
position: absolute;
|
|
271
|
+
top: 0;
|
|
272
|
+
left: 0;
|
|
273
|
+
right: 0;
|
|
274
|
+
height: 28px;
|
|
275
|
+
background: #e1e4e8;
|
|
276
|
+
color: #586069;
|
|
277
|
+
font-size: 12px;
|
|
278
|
+
font-weight: 600;
|
|
279
|
+
padding: 0 16px;
|
|
280
|
+
display: flex;
|
|
281
|
+
align-items: center;
|
|
282
|
+
border-radius: 6px 6px 0 0;
|
|
283
|
+
text-transform: uppercase;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.markdown-body pre:not([data-language]) {
|
|
287
|
+
padding: 16px !important;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.markdown-body pre code {
|
|
291
|
+
padding: 0 !important;
|
|
292
|
+
background: transparent !important;
|
|
277
293
|
}
|
|
278
294
|
|
|
279
295
|
/* Table Styles */
|
|
@@ -438,14 +454,16 @@
|
|
|
438
454
|
// Determine role class based on specific role string
|
|
439
455
|
// "HAL9000" -> robot
|
|
440
456
|
// Anything else -> human
|
|
441
|
-
const roleClass = msg.role
|
|
457
|
+
const roleClass = msg.role.includes('HAL9000') ? 'robot' : 'human';
|
|
442
458
|
row.className = `message-row ${roleClass}`;
|
|
443
459
|
|
|
444
460
|
const group = document.createElement('div');
|
|
445
461
|
group.className = 'message-group';
|
|
446
462
|
|
|
447
463
|
// Mapping sender name
|
|
448
|
-
const senderName =
|
|
464
|
+
const senderName = /^\p{Extended_Pictographic}/u.test(msg.role)
|
|
465
|
+
? msg.role
|
|
466
|
+
: (msg.role.includes('HAL9000') ? `🤖 ${msg.role}` : `😺 ${msg.role}`);
|
|
449
467
|
|
|
450
468
|
const bubble = document.createElement('div');
|
|
451
469
|
bubble.className = 'message-bubble';
|
|
@@ -476,8 +494,42 @@
|
|
|
476
494
|
|
|
477
495
|
// Content
|
|
478
496
|
const contentDiv = document.createElement('div');
|
|
479
|
-
contentDiv.className = 'message-content';
|
|
480
|
-
contentDiv.innerHTML = marked.parse(msg.text || '');
|
|
497
|
+
contentDiv.className = 'message-content markdown-body';
|
|
498
|
+
contentDiv.innerHTML = marked.parse(msg.text || '');
|
|
499
|
+
|
|
500
|
+
// Highlight Code
|
|
501
|
+
contentDiv.querySelectorAll('pre code').forEach((block) => {
|
|
502
|
+
// Trim think block content
|
|
503
|
+
if (block.classList.contains('language-think')) {
|
|
504
|
+
block.textContent = block.textContent.trim();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
hljs.highlightElement(block);
|
|
508
|
+
|
|
509
|
+
// Detect language and set attribute
|
|
510
|
+
let lang = '';
|
|
511
|
+
block.classList.forEach(cls => {
|
|
512
|
+
if (cls.startsWith('language-')) {
|
|
513
|
+
lang = cls.replace('language-', '');
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
if (lang) {
|
|
517
|
+
block.parentElement.setAttribute('data-language', lang);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (block.classList.contains('language-think')) {
|
|
521
|
+
const pre = block.parentElement;
|
|
522
|
+
pre.classList.add('think-block');
|
|
523
|
+
pre.style.whiteSpace = 'pre-wrap';
|
|
524
|
+
pre.style.wordBreak = 'break-word';
|
|
525
|
+
block.style.whiteSpace = 'pre-wrap';
|
|
526
|
+
block.style.wordBreak = 'break-word';
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// Open links in new tab
|
|
531
|
+
contentDiv.querySelectorAll('a').forEach(a => a.target = '_blank');
|
|
532
|
+
|
|
481
533
|
bubble.appendChild(contentDiv);
|
|
482
534
|
|
|
483
535
|
group.appendChild(bubble);
|
|
@@ -495,17 +547,15 @@
|
|
|
495
547
|
|
|
496
548
|
// Check if it is the last message
|
|
497
549
|
if (index === chatData.messages.length - 1) {
|
|
498
|
-
// For the last message, scroll to its top (start) with a small offset
|
|
499
550
|
const lastMsgElement = document.getElementById('chatContainer').lastElementChild;
|
|
500
551
|
if (lastMsgElement) {
|
|
501
|
-
|
|
502
|
-
top: lastMsgElement.
|
|
552
|
+
window.scrollTo({
|
|
553
|
+
top: lastMsgElement.getBoundingClientRect().top + window.scrollY - 100,
|
|
503
554
|
behavior: 'smooth'
|
|
504
555
|
});
|
|
505
556
|
}
|
|
506
557
|
} else {
|
|
507
|
-
|
|
508
|
-
main.scrollTop = main.scrollHeight;
|
|
558
|
+
window.scrollTo(0, document.body.scrollHeight);
|
|
509
559
|
}
|
|
510
560
|
}, delay);
|
|
511
561
|
delay += 800; // Keep delay effect
|
package/web/turn.mjs
CHANGED
|
@@ -1,30 +1,104 @@
|
|
|
1
|
-
import { dbio, hal, utilitas } from '../index.mjs';
|
|
1
|
+
import { callosum, dbio, hal, utilitas } from '../index.mjs';
|
|
2
2
|
import { readFile } from 'fs/promises';
|
|
3
3
|
|
|
4
4
|
const getPath = (subPath) => utilitas.__(import.meta.url, subPath);
|
|
5
5
|
const getHtml = async () => await readFile(getPath('turn.html'), 'utf-8');
|
|
6
6
|
const renderHtml = async (data) => await getHtml().then((html) => html.replace("'{{data}}'", data));
|
|
7
7
|
|
|
8
|
+
const file = async (ctx) => {
|
|
9
|
+
try {
|
|
10
|
+
const url = await callosum.call('getFileLink', { args: [ctx.params.id] });
|
|
11
|
+
const resp = await fetch(url);
|
|
12
|
+
if (!resp.ok) { throw new Error(`Fetch failed: ${resp.status}`); }
|
|
13
|
+
ctx.set('Content-Type', resp.headers.get('Content-Type'));
|
|
14
|
+
ctx.body = Buffer.from(await resp.arrayBuffer());
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.error('Error serving file:', ctx.params.id, err);
|
|
17
|
+
ctx.status = 404;
|
|
18
|
+
ctx.body = 'Not Found';
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
8
22
|
const process = async (ctx, next) => {
|
|
9
23
|
const result = await dbio.queryOne(
|
|
10
24
|
`SELECT * FROM ${hal.table} WHERE token = $1`,
|
|
11
25
|
[ctx.params.token]
|
|
12
26
|
);
|
|
27
|
+
if (!result) { return await next(); }
|
|
13
28
|
const prompt_count = await dbio.countAll(hal.table);
|
|
14
29
|
result.received = JSON.parse(result.received);
|
|
15
30
|
result.response = JSON.parse(result.response);
|
|
16
|
-
|
|
31
|
+
|
|
32
|
+
const msg = result.received.message;
|
|
33
|
+
let userText = result.received_text || '';
|
|
34
|
+
|
|
35
|
+
const p = msg.photo?.[msg.photo?.length - 1];
|
|
36
|
+
if (p) { userText = `\n\n${userText}`; }
|
|
37
|
+
|
|
38
|
+
const a = msg.audio || msg.voice;
|
|
39
|
+
if (a) { userText = `<audio controls src="/file/${a.file_id}" style="width: 100%; display: block; margin-bottom: 8px;"></audio>\n\n${userText}`; }
|
|
40
|
+
|
|
41
|
+
const v = msg.video || msg.video_note;
|
|
42
|
+
if (v) { userText = `<video controls src="/file/${v.file_id}" style="width: 100%; display: block; border-radius: 8px; margin-bottom: 8px;"></video>\n\n${userText}`; }
|
|
43
|
+
|
|
17
44
|
const messages = [{
|
|
18
|
-
role: `${
|
|
19
|
-
text:
|
|
20
|
-
time: new Date(
|
|
45
|
+
role: `${msg.from.username} (${msg.from.first_name} ${msg.from.last_name})`,
|
|
46
|
+
text: userText.trim(),
|
|
47
|
+
time: new Date(msg.date * 1000),
|
|
21
48
|
}];
|
|
22
|
-
|
|
49
|
+
|
|
50
|
+
const first = result.response?.[0];
|
|
51
|
+
let modelLine = first?.text?.split('\n')?.[0] || '';
|
|
52
|
+
modelLine = modelLine.includes('/') ? modelLine.replace(/:.*$/g, '') : '';
|
|
53
|
+
let role = 'HAL9000';
|
|
54
|
+
if (/^\p{Extended_Pictographic}/u.test(modelLine)) {
|
|
55
|
+
role = `${modelLine.split(' ')[0]} ${role}`;
|
|
56
|
+
modelLine = modelLine.split(' ').slice(1).join(' ');
|
|
57
|
+
}
|
|
58
|
+
role = `${role} (${modelLine})`;
|
|
59
|
+
|
|
60
|
+
const last = result.response?.[result.response?.length - 1];
|
|
61
|
+
const defaultTime = new Date((last?.edit_date || last?.date || result.received.message.date) * 1000);
|
|
62
|
+
|
|
63
|
+
if (result.response_text) {
|
|
23
64
|
messages.push({
|
|
24
|
-
role
|
|
25
|
-
text:
|
|
26
|
-
time:
|
|
65
|
+
role,
|
|
66
|
+
text: result.response_text,
|
|
67
|
+
time: defaultTime,
|
|
27
68
|
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
result.response.map(x => {
|
|
72
|
+
const p = x.photo?.[x.photo?.length - 1];
|
|
73
|
+
if (p) {
|
|
74
|
+
let text = ``;
|
|
75
|
+
if (x.caption) { text += `\n\n${x.caption}`; }
|
|
76
|
+
messages.push({
|
|
77
|
+
role,
|
|
78
|
+
text,
|
|
79
|
+
time: new Date((x.edit_date || x.date || result.received.message.date) * 1000),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const a = x.audio || x.voice;
|
|
83
|
+
if (a) {
|
|
84
|
+
let text = `<audio controls src="/file/${a.file_id}" style="width: 100%; display: block;"></audio>`;
|
|
85
|
+
if (x.caption) { text += `\n\n${x.caption}`; }
|
|
86
|
+
messages.push({
|
|
87
|
+
role,
|
|
88
|
+
text,
|
|
89
|
+
time: new Date((x.edit_date || x.date || result.received.message.date) * 1000),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const v = x.video || x.video_note;
|
|
93
|
+
if (v) {
|
|
94
|
+
let text = `<video controls src="/file/${v.file_id}" style="width: 100%; display: block; border-radius: 8px;"></video>`;
|
|
95
|
+
if (x.caption) { text += `\n\n${x.caption}`; }
|
|
96
|
+
messages.push({
|
|
97
|
+
role,
|
|
98
|
+
text,
|
|
99
|
+
time: new Date((x.edit_date || x.date || result.received.message.date) * 1000),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
28
102
|
});
|
|
29
103
|
ctx.body = await renderHtml(JSON.stringify({
|
|
30
104
|
bot_id: result.bot_id, chat_id: result.chat_id,
|
|
@@ -39,5 +113,10 @@ export const { actions } = {
|
|
|
39
113
|
method: 'GET',
|
|
40
114
|
process,
|
|
41
115
|
},
|
|
116
|
+
{
|
|
117
|
+
path: 'file/:id',
|
|
118
|
+
method: 'GET',
|
|
119
|
+
process: file,
|
|
120
|
+
},
|
|
42
121
|
]
|
|
43
122
|
};
|