kenken999 commited on
Commit
739e03a
1 Parent(s): c21e43e
Files changed (3) hide show
  1. staticfiles/aivtuber.js +1 -0
  2. staticfiles/livetest.html +570 -0
  3. text.txt +1 -1
staticfiles/aivtuber.js CHANGED
@@ -235,6 +235,7 @@ const getNextComment = () => {
235
  }
236
 
237
  const handleNewLiveCommentIfNeeded = async () => {
 
238
  if (liveCommentQueues.length == 0) {
239
  // QUEUEがなければ何もしない
240
  setTimeout(handleNewLiveCommentIfNeeded, INTERVAL_MILL_SECONDS_HANDLING_COMMENTS);
 
235
  }
236
 
237
  const handleNewLiveCommentIfNeeded = async () => {
238
+ alert("get live chat");
239
  if (liveCommentQueues.length == 0) {
240
  // QUEUEがなければ何もしない
241
  setTimeout(handleNewLiveCommentIfNeeded, INTERVAL_MILL_SECONDS_HANDLING_COMMENTS);
staticfiles/livetest.html ADDED
@@ -0,0 +1,570 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width">
7
+ <title>Sample Chat AI VTuber</title>
8
+ <link rel="stylesheet" href="aivtuber.css">
9
+ <script src="./aivtuber.js"></script>
10
+ <title>Live2Dサンプル</title>
11
+ <!-- Live2D -->
12
+ <script src="./live2dcubismcore.js"></script>
13
+ <script src="//cdn.jsdelivr.net/gh/dylanNew/live2d/webgl/Live2D/lib/live2d.min.js"></script>
14
+ <!-- PixiJS -->
15
+ <script src="//cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min.js"></script>
16
+ <script src="//cdn.jsdelivr.net/npm/pixi-live2d-display/dist/index.min.js"></script>
17
+ <!-- Kalidokit -->
18
+ <script src="//cdn.jsdelivr.net/npm/kalidokit@1.1/dist/kalidokit.umd.js"></script>
19
+ </head>
20
+
21
+ <body>
22
+
23
+ <video id="my-video" autoplay></video>
24
+
25
+ <script>
26
+
27
+ 'use strict';
28
+ class MiiboAvatar {
29
+ constructor(config) {
30
+ this.container = config.container;
31
+ this.speechToTextOptions = config.option.speech_to_text;
32
+ this.miiboOptions = config.option.miibo;
33
+ this.didOptions = config.option.d_id;
34
+ this.initialize();
35
+ }
36
+
37
+ initialize() {
38
+ alert("38");
39
+ const RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
40
+
41
+ this.videoElement = document.getElementById(this.container);
42
+ this.videoElement.setAttribute('playsinline', '');
43
+ this.playIdleVideo();
44
+
45
+ this.createNewStream();
46
+ this.rec = new webkitSpeechRecognition()
47
+
48
+ this.speaching = false;
49
+ this.processing = false;
50
+ this.streams = [];
51
+ }
52
+
53
+ async createNewStream() {
54
+ alert("53")
55
+ try {
56
+ this.stopAllStreams();
57
+ this.closePC();
58
+
59
+ let presenter = {"source_url": this.didOptions.presenter.image_url}
60
+ const sessionResponse = await this.fetchWithRetries(`https://api.d-id.com/${this.didOptions.service}/streams`, {
61
+ method: 'POST',
62
+ headers: {
63
+ Authorization: `Basic ${this.didOptions.key}`,
64
+ 'Content-Type': 'application/json',
65
+ },
66
+ body: JSON.stringify(presenter),
67
+ });
68
+
69
+ const { id: newStreamId, offer, ice_servers: iceServers, session_id: newSessionId } = await sessionResponse.json();
70
+ this.streamId = newStreamId;
71
+ this.sessionId = newSessionId;
72
+
73
+ try {
74
+ this.sessionClientAnswer = await this.createPeerConnection(offer, iceServers);
75
+ } catch (e) {
76
+ console.log('Error creating peer connection:', e);
77
+ this.stopAllStreams();
78
+ this.closePC();
79
+ return;
80
+ }
81
+
82
+ const sdpResponse = await fetch(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}/sdp`, {
83
+ method: 'POST',
84
+ headers: {
85
+ Authorization: `Basic ${this.didOptions.key}`,
86
+ 'Content-Type': 'application/json',
87
+ },
88
+ body: JSON.stringify({
89
+ answer: this.sessionClientAnswer,
90
+ session_id: this.sessionId,
91
+ }),
92
+ });
93
+
94
+ // Handle sdpResponse if needed
95
+ } catch (error) {
96
+ console.log('Error creating new stream:', error);
97
+ // Handle error
98
+ }
99
+ }
100
+
101
+ speechRecogInit() {
102
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)()
103
+
104
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
105
+ navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
106
+ var input = this.audioContext.createMediaStreamSource(stream)
107
+ this.audioContext.resume()
108
+ this.recorder = new Recorder(input)
109
+ })
110
+ }
111
+ }
112
+
113
+
114
+ startRecording() {
115
+ this.recorder && this.recorder.record();
116
+ }
117
+
118
+ stopRecording() {
119
+ this.playLoadingVideo();
120
+ this.recorder && this.recorder.stop();
121
+ this.audioRecognize();
122
+ this.recorder.clear();
123
+ }
124
+
125
+ audioRecognize() {
126
+ this.recorder && this.recorder.exportWAV((blob) => {
127
+ let reader = new FileReader()
128
+ reader.onload = () => {
129
+ let result = new Uint8Array(reader.result)
130
+
131
+ let data = {
132
+ "config": {
133
+ "encoding": "LINEAR16",
134
+ "languageCode": "ja-JP",
135
+ "alternativeLanguageCodes": ["en-US"],//,"cmn-CN","ko-KR"],
136
+ "audio_channel_count": 2
137
+ },
138
+ "audio": {
139
+ "content": this.arrayBufferToBase64(result)
140
+ }
141
+ }
142
+ fetch('https://speech.googleapis.com/v1/speech:recognize?key=' + this.speechToTextOptions.api_key, {
143
+ method: 'POST',
144
+ headers: {
145
+ 'Content-Type': 'application/json; charset=utf-8'
146
+ },
147
+ body: JSON.stringify(data)
148
+ }).then((response) => {
149
+ return response.text()
150
+ }).then((text) => {
151
+ let result_json = JSON.parse(text)
152
+ text = result_json.results[0].alternatives[0].transcript;
153
+ this.languageCode = result_json.results[0].languageCode;
154
+ this.ask(text)
155
+ })
156
+ }
157
+ reader.readAsArrayBuffer(blob)
158
+ })
159
+ }
160
+ // Chrome Only
161
+ autoRecognizeMessage(message) {
162
+ handleLiveComment("test",message)
163
+ alert("start word test")
164
+ this.rec.continuous = false
165
+ this.rec.interimResults = false
166
+ this.rec.lang = 'ja-JP'
167
+ this.processing = true
168
+ this.playLoadingVideo();
169
+
170
+ this.rec.stop()
171
+ alert("startssssssssssssssssssssssssss")
172
+ const { transcript } = message
173
+ this.ask(transcript);
174
+ //for (var i = e.resultIndex; i < e.results.length; i++) {
175
+ // if (!e.results[i].isFinal) continue
176
+ //
177
+ // const { transcript } = e.results[i][0]
178
+ // this.ask(transcript);
179
+ //}
180
+ //this.rec.onresult = (e) => {
181
+
182
+ //}
183
+
184
+ this.rec.onend = () => { this.autoRecognizeRestart() }
185
+ this.rec.start()
186
+ }
187
+ // Chrome Only
188
+ autoRecognize() {
189
+ this.rec.continuous = false
190
+ this.rec.interimResults = false
191
+ this.rec.lang = 'ja-JP'
192
+
193
+ this.rec.onresult = (e) => {
194
+ this.processing = true
195
+ this.playLoadingVideo();
196
+
197
+ this.rec.stop()
198
+ alert("start")
199
+
200
+ for (var i = e.resultIndex; i < e.results.length; i++) {
201
+ if (!e.results[i].isFinal) continue
202
+
203
+ const { transcript } = e.results[i][0]
204
+ this.ask(transcript);
205
+ }
206
+ }
207
+
208
+ this.rec.onend = () => { this.autoRecognizeRestart() }
209
+ this.rec.start()
210
+ }
211
+
212
+ autoRecognizeRestart() {
213
+ if (this.processing) {
214
+ setTimeout(() => {this.autoRecognizeRestart()}, 1000)
215
+ } else {
216
+ this.rec.start()
217
+ }
218
+ }
219
+
220
+ arrayBufferToBase64(buffer) {
221
+ let binary = ''
222
+ let bytes = new Uint8Array(buffer);
223
+ let len = bytes.byteLength
224
+ for (let i = 0; i < len; i++) {
225
+ binary += String.fromCharCode(bytes[i])
226
+ }
227
+ return window.btoa(binary)
228
+ }
229
+
230
+ ask(message) {
231
+ //youtubeからの質問
232
+ alert("203 ask"+message);
233
+ //ask(start);
234
+ this.getMiiboResponse(message);
235
+ }
236
+
237
+ async getMiiboResponse(utterance) {
238
+ alert("209 getMiiboResponse"+utterance)
239
+ const params = {
240
+ api_key: this.miiboOptions.api_key,
241
+ agent_id: this.miiboOptions.agent_id,
242
+ uid: this.miiboOptions.user_id,
243
+ stream: true,
244
+ utterance: utterance
245
+ };
246
+
247
+ try {
248
+ const res = await fetch("https://api-mebo.dev/api", {
249
+ method: "POST",
250
+ headers: { "Content-Type": "application/json" },
251
+ body: JSON.stringify(params),
252
+ });
253
+
254
+ const reader = res.body.getReader();
255
+ const decoder = new TextDecoder();
256
+ let output = "";
257
+ let sentences = [];
258
+ let current_index = 0;
259
+
260
+ const read = async () => {
261
+ const { done, value } = await reader.read();
262
+ if (done) return;
263
+
264
+ let dataString = decoder.decode(value).split("\n").filter(x => x != "");
265
+
266
+ try {
267
+ let responseData = JSON.parse(dataString[dataString.length - 1]);
268
+
269
+ output = responseData.bestResponse.utterance.split("\n").filter(x => x.trim() != "").join("\n");
270
+ sentences = output.replace(/[。、\.\,\!\?!?]/,".").split(".")
271
+ if (this.didOptions.priority == "speed" && current_index == 0 && current_index + 1 < sentences.length) {
272
+ this.startTalk(sentences[current_index++])
273
+ }
274
+ } catch(e) {
275
+ console.log(e);
276
+ }
277
+
278
+ return read();
279
+ };
280
+
281
+ await read();
282
+ reader.releaseLock();
283
+
284
+ this.startTalk(sentences.slice(current_index).join("。"));
285
+ } catch(error) {
286
+ console.log("Error fetching AI response: ", error);
287
+ }
288
+ }
289
+
290
+ async startTalk(input) {
291
+ if (this.peerConnection?.signalingState === 'stable' || this.peerConnection?.iceConnectionState === 'connected') {
292
+
293
+ const gender = this.didOptions.presenter.gender;
294
+ let voice_id = this.didOptions.presenter.voice_id || "";
295
+
296
+ if (voice_id == "") {
297
+ switch (this.languageCode) {
298
+ case "en-us":
299
+ voice_id = gender == "male" ? "en-US-GuyNeural" : "en-US-AriaNeural"
300
+ break;
301
+ case "ko-kr":
302
+ voice_id = gender == "male" ? "ko-KR-InJoonNeural" : "ko-KR-YuJinNeural"
303
+ break;
304
+ case "cmn-CN":
305
+ voice_id = gender == "male" ? "zh-CN-YunjianNeural" : "zh-CN-XiaohanNeural"
306
+ break;
307
+ default:
308
+ voice_id = gender == "male" ? "ja-JP-KeitaNeural" : "ja-JP-NanamiNeural"
309
+ }
310
+ }
311
+
312
+ const requestOptions = {
313
+ method: 'POST',
314
+ headers: {
315
+ Authorization: `Basic ${this.didOptions.key}`,
316
+ 'Content-Type': 'application/json',
317
+ },
318
+ body: JSON.stringify({
319
+ script: {
320
+ type: "text",
321
+ subtitles: false,
322
+ provider: {
323
+ type: "microsoft",
324
+ voice_id: voice_id
325
+ },
326
+ ssml: false,
327
+ input: input
328
+ },
329
+ config: {
330
+ fluent: false,
331
+ pad_audio: 0,
332
+ align_driver: false,
333
+ stitch: false,
334
+ auto_match: false,
335
+ sharpen: false,
336
+ normalization_factor: 0
337
+ },
338
+ session_id: this.sessionId,
339
+ }),
340
+ };
341
+
342
+ if (this.didOptions.service === 'clips') {
343
+ requestOptions.body.background = { color: '#FFFFFF' };
344
+ }
345
+
346
+ try {
347
+ const playResponse = await this.fetchWithRetries(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}`, requestOptions);
348
+ // Handle response if needed
349
+ } catch (error) {
350
+ console.error('Error starting talk:', error);
351
+ // Handle error
352
+ }
353
+ }
354
+ }
355
+
356
+ async destoryTalk(input) {
357
+ try {
358
+ await fetch(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}`, {
359
+ method: 'DELETE',
360
+ headers: {
361
+ Authorization: `Basic ${this.didOptions.key}`,
362
+ 'Content-Type': 'application/json',
363
+ },
364
+ body: JSON.stringify({ session_id: this.sessionId }),
365
+ });
366
+
367
+ await this.stopAllStreams();
368
+ await this.closePC();
369
+ } catch (error) {
370
+ console.error('Error destroying talk:', error);
371
+ // Handle error
372
+ }
373
+ }
374
+
375
+ onIceCandidate(event) {
376
+ if (event.candidate) {
377
+ const { candidate, sdpMid, sdpMLineIndex } = event.candidate;
378
+
379
+ fetch(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}/ice`, {
380
+ method: 'POST',
381
+ headers: {
382
+ Authorization: `Basic ${this.didOptions.key}`,
383
+ 'Content-Type': 'application/json',
384
+ },
385
+ body: JSON.stringify({
386
+ candidate,
387
+ sdpMid,
388
+ sdpMLineIndex,
389
+ session_id: this.sessionId,
390
+ }),
391
+ }).catch((error) => {
392
+ console.error('Error sending ICE candidate:', error);
393
+ // Handle error
394
+ });
395
+ }
396
+ }
397
+
398
+ onIceConnectionStateChange() {
399
+ if (this.peerConnection.iceConnectionState === 'failed' || this.peerConnection.iceConnectionState === 'closed') {
400
+ this.stopAllStreams();
401
+ this.closePC();
402
+ }
403
+ }
404
+
405
+ onTrack(event) {
406
+ if (!event.track || !event.streams || event.streams.length === 0) return;
407
+
408
+ this.statsIntervalId = setInterval(async () => {
409
+ const stats = await this.peerConnection.getStats(event.track);
410
+ stats.forEach((report) => {
411
+ if (report.type === 'inbound-rtp' && report.mediaType === 'video') {
412
+ const videoStatusChanged = this.videoIsPlaying !== report.bytesReceived > this.lastBytesReceived;
413
+
414
+ if (videoStatusChanged) {
415
+ this.videoIsPlaying = report.bytesReceived > this.lastBytesReceived;
416
+ this.onVideoStatusChange(this.videoIsPlaying, event.streams[0]);
417
+ }
418
+ this.lastBytesReceived = report.bytesReceived;
419
+ }
420
+ });
421
+ }, 100);
422
+ }
423
+
424
+ onVideoStatusChange(videoIsPlaying, stream) {
425
+ let status;
426
+ if (videoIsPlaying) {
427
+ status = 'streaming';
428
+ const remoteStream = stream;
429
+ this.streams.push(remoteStream);
430
+ this.checkSpeaching();
431
+ } else {
432
+ status = 'empty';
433
+ this.speaching = false;
434
+ this.processing = false;
435
+ this.playIdleVideo();
436
+ }
437
+ }
438
+
439
+ checkSpeaching() {
440
+ if (this.speaching) {
441
+ setTimeout(() => {this.checkSpeaching()}, 20)
442
+ } else {
443
+ this.setVideoElement(this.streams.shift());
444
+ }
445
+ }
446
+
447
+ async createPeerConnection(offer, iceServers) {
448
+ if (!this.peerConnection) {
449
+ this.peerConnection = new RTCPeerConnection({ iceServers });
450
+ this.peerConnection.addEventListener('icecandidate', this.onIceCandidate.bind(this), true);
451
+ this.peerConnection.addEventListener('iceconnectionstatechange', this.onIceConnectionStateChange.bind(this), true);
452
+ this.peerConnection.addEventListener('track', this.onTrack.bind(this), true);
453
+ }
454
+
455
+ await this.peerConnection.setRemoteDescription(offer);
456
+ const sessionClientAnswer = await this.peerConnection.createAnswer();
457
+ await this.peerConnection.setLocalDescription(sessionClientAnswer);
458
+ return sessionClientAnswer;
459
+ }
460
+
461
+
462
+ setVideoElement(stream) {
463
+ if (!stream) return;
464
+ this.videoElement.srcObject = stream;
465
+ this.videoElement.loop = false;
466
+
467
+ // safari hotfix
468
+ if (this.videoElement.paused) {
469
+ this.videoElement
470
+ .play()
471
+ .then((_) => {})
472
+ .catch((e) => {});
473
+ }
474
+ }
475
+
476
+ playIdleVideo() {
477
+ this.videoElement.srcObject = undefined;
478
+ this.videoElement.src = this.didOptions.presenter.idle_movie;
479
+ this.videoElement.loop = true;
480
+ }
481
+
482
+ playLoadingVideo() {
483
+ this.videoElement.srcObject = undefined;
484
+ this.videoElement.src = this.didOptions.presenter.loading_movie;
485
+ this.videoElement.loop = false;
486
+ }
487
+
488
+ stopAllStreams() {
489
+ if (this.videoElement.srcObject) {
490
+ this.videoElement.srcObject.getTracks().forEach((track) => track.stop());
491
+ this.videoElement.srcObject = null;
492
+ }
493
+ }
494
+
495
+ closePC(pc = this.peerConnection) {
496
+ if (!pc) return;
497
+ pc.close();
498
+ pc.removeEventListener('icecandidate', this.onIceCandidate.bind(this), true);
499
+ pc.removeEventListener('iceconnectionstatechange', this.onIceConnectionStateChange.bind(this), true);
500
+ pc.removeEventListener('track', this.onTrack.bind(this), true);
501
+ clearInterval(this.statsIntervalId);
502
+ if (pc === this.peerConnection) {
503
+ this.peerConnection = null;
504
+ }
505
+ }
506
+
507
+ async fetchWithRetries(url, options, retries = 1) {
508
+ const maxRetryCount = 3;
509
+ const maxDelaySec = 4;
510
+ try {
511
+ return await fetch(url, options);
512
+ } catch (err) {
513
+ if (retries <= maxRetryCount) {
514
+ const delay = Math.min(Math.pow(2, retries) / 4 + Math.random(), maxDelaySec) * 1000;
515
+ await new Promise((resolve) => setTimeout(resolve, delay));
516
+ return this.fetchWithRetries(url, options, retries + 1);
517
+ } else {
518
+ throw new Error(`Max retries exceeded. error: ${err}`);
519
+ }
520
+ }
521
+ }
522
+ }
523
+
524
+ const miiboAvatar = new MiiboAvatar({
525
+ container: "my-video",
526
+ option: {
527
+ miibo: {
528
+ api_key: "ed212f05-a0f2-4838-9bb3-26d318504a76190c9e5bc19f0",
529
+ agent_id: "3e83a6b2-3c03-42b1-abc7-8a2dd97a8999190c7d1806120c",
530
+ user_id: "user",
531
+ },
532
+ d_id: {
533
+ key: "bWl5YXRha2VuOTk4QGdtYWlsLmNvbQ:2YVtXHVy3BYRW0AHvYbPa",
534
+ service: "talks",
535
+ priority: "cost",
536
+ presenter:{
537
+ gender: "male",
538
+ image_url: "https://kenken999-fastapi-django-main-live.hf.space/static/sugi.jpg",
539
+ idle_movie: "https://github.com/miibo-takumori/resorces/raw/main/portorate-idle.mp4",
540
+ loading_movie: "https://github.com/miibo-takumori/resorces/raw/main/portorate-loading.mp4",
541
+ }
542
+ }
543
+ }
544
+ })
545
+
546
+ miiboAvatar.autoRecognize();
547
+ </script>
548
+ <canvas id="my-live2d"></canvas>
549
+ <script src="live2d.js"></script>
550
+ <script src="index.js"></script>
551
+ <div id="vtubers">
552
+ <!--
553
+ <iframe width="auto" height="315" src="https://www.youtube.com/embed/9BSgttQDyOw?si=ax2F4oAi2h9f9eGT" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
554
+ /-->
555
+ <!--<img id="charaImg" src="chara.png" width="auto" height="400" />/-->
556
+ </div>
557
+ <div id="aiResponse" class="aiResponseBox">
558
+ <p class="ai-response" id="aiResponseUtterance"></p>
559
+ </div>
560
+ <div class="bottomBox">
561
+ <p id="userComment"></p>
562
+ <button id="startLiveButton" onclick="startLive();">LIVE開始</button>
563
+ <div id="submit_form">
564
+ <input type="text" id="utterance" />
565
+ <button id="sendButton" onclick="onClickSend();">送信</button>
566
+ </div>
567
+ </div>
568
+ </body>
569
+
570
+ </html>
text.txt CHANGED
@@ -1 +1 @@
1
- 電話番号
 
1
+ いまいち買取額想像出来ないのですが結構安いってことですか?