From bab8528db1a0a7cca8cc3e81dcf7d0c4438956c4 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Sat, 15 Jun 2024 17:32:12 +0300 Subject: [PATCH 1/7] try to setup demo --- .env.demo | 6 +++--- .gitignore | 1 + Dockerfile | 3 ++- _doc/_notes/readme.md | 4 ++++ docker-compose.demo.yml | 3 ++- web/chat-server.js | 10 +++++----- web/deploy/demo.Dockerfile | 4 +++- 7 files changed, 20 insertions(+), 11 deletions(-) diff --git a/.env.demo b/.env.demo index fea3dc7..2989d1a 100644 --- a/.env.demo +++ b/.env.demo @@ -17,7 +17,7 @@ LNN_API_URL=https://ollama.d-popov.com/api/generate GROQ_API_KEY=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 OPENAI_API_KEY=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 -WS_URL=wss://tts.d-popov.com +PUBLIC_WS_URL=wss://ws.tts.d-popov.com PUBLIC_HOSTNAME=tts.d-popov.com -SERVER_PORT_HTTP=8080 -SERVER_PORT_WS=8081 \ No newline at end of file +SERVER_PORT_HTTP=28080 +SERVER_PORT_WS=28081 \ No newline at end of file diff --git a/.gitignore b/.gitignore index b6c8ffa..6bc38ed 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ agent-mobile/artimobile/supervisord.pid agent-pyter/lag-llama agent-pyter/google-chrome-stable_current_amd64.deb web/.node-persist/* +.node-persist/storage/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 3cb3638..5cb5705 100644 --- a/Dockerfile +++ b/Dockerfile @@ -90,5 +90,6 @@ RUN . /venv/bin/activate && pip install --upgrade pip && pip install -r agent-py # CMD ["npm", "start"] -CMD ["npm", "run", "start:demo"] +# CMD ["npm", "run", "start:demo"] +CMD ["npm", "run", "start:demo-chat"] #CMD ["npm", "run", "start:tele"] \ No newline at end of file diff --git a/_doc/_notes/readme.md b/_doc/_notes/readme.md index 25e9ed8..1179cb1 100644 --- a/_doc/_notes/readme.md +++ b/_doc/_notes/readme.md @@ -1,3 +1,7 @@ +# .10: /mnt/apps/DEV/REPOS/git.d-popov.com/ai-kevin/ +# run +export NODE_ENV=demo && npm run start:demo-chat + # build: docker build -t kevin-ai . # start the project in container: diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml index 0cc78d3..0b97d0c 100644 --- a/docker-compose.demo.yml +++ b/docker-compose.demo.yml @@ -7,7 +7,8 @@ services: context: . dockerfile: web/deploy/demo.Dockerfile ports: - - "8880:8080" # Exposes port 3000 on the host and maps it to port 3000 on the container + - "28880:8080" + - "28881:8081" volumes: - .:/usr/src/app # Mounts the current directory to /usr/src/app in the container environment: diff --git a/web/chat-server.js b/web/chat-server.js index 6585318..8cf4449 100644 --- a/web/chat-server.js +++ b/web/chat-server.js @@ -343,11 +343,11 @@ app.post('/log', (req, res) => { }); app.get('/wsurl', (req, res) => { - if(process.env.PUBLIC_HOSTNAME){ - process.env.WS_URL = `wss://${process.env.PUBLIC_HOSTNAME}` - } - console.log('Request for WS URL resolved with:', process.env.WS_URL ); - res.status(200).send(process.env.WS_URL); + // if(process.env.PUBLIC_HOSTNAME){ + // process.env.WS_URL = `wss://${process.env.PUBLIC_HOSTNAME}` + // } + console.log('Request for WS URL resolved with:', process.env.PUBLIC_WS_URL ); + res.status(200).send(process.env.PUBLIC_WS_URL); }); app.get('/settings', async (req, res) => { diff --git a/web/deploy/demo.Dockerfile b/web/deploy/demo.Dockerfile index 484a3ad..48a281c 100644 --- a/web/deploy/demo.Dockerfile +++ b/web/deploy/demo.Dockerfile @@ -4,6 +4,8 @@ FROM node:20 # Create and change to the app directory WORKDIR /usr/src/app +ENV NODE_ENV=demo + # Copy the package.json and package-lock.json files COPY package*.json ./ @@ -13,8 +15,8 @@ RUN npm install # Copy the rest of the application code COPY . . -# Expose port 3000 EXPOSE 8880 +EXPOSE 8881 # Start the application CMD ["npm", "run", "start:demo-chat"] From ffca907f3967712a893be9fa9c880675a9227295 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Sat, 15 Jun 2024 18:10:45 +0300 Subject: [PATCH 2/7] worling demo setup --- .env.demo | 12 +- docker-compose.demo.yml | 32 +- web/chat-server.js | 702 ++++++++++++++++++++++------------------ 3 files changed, 415 insertions(+), 331 deletions(-) diff --git a/.env.demo b/.env.demo index 2989d1a..554a865 100644 --- a/.env.demo +++ b/.env.demo @@ -6,7 +6,9 @@ # SERVER_PORT_HTTP=8080 ENV_NAME=demo -TTS_API_URL=https://api.tts.d-popov.com/asr +# TTS_API_URL=https://api.tts.d-popov.com/asr +# TTS_API_URL=https://api.tts.d-popov.com/asr +# TTS_BACKEND=http://192.168.0.11:9009/asr # LLN_MODEL=qwen2 # LNN_API_URL=https://ollama.d-popov.com/api/generate @@ -17,7 +19,7 @@ LNN_API_URL=https://ollama.d-popov.com/api/generate GROQ_API_KEY=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 OPENAI_API_KEY=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 -PUBLIC_WS_URL=wss://ws.tts.d-popov.com -PUBLIC_HOSTNAME=tts.d-popov.com -SERVER_PORT_HTTP=28080 -SERVER_PORT_WS=28081 \ No newline at end of file +# PUBLIC_WS_URL=wss://ws.tts.d-popov.com +# PUBLIC_HOSTNAME=tts.d-popov.com +# SERVER_PORT_HTTP=28080 +# SERVER_PORT_WS=28081 \ No newline at end of file diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml index 0b97d0c..03ab51c 100644 --- a/docker-compose.demo.yml +++ b/docker-compose.demo.yml @@ -1,16 +1,22 @@ -version: '3.8' - services: - node-app: - container_name: node-voice-chat - build: - context: . - dockerfile: web/deploy/demo.Dockerfile - ports: - - "28880:8080" - - "28881:8081" + chat-server: + image: node:20-alpine + container_name: ml-voice-chat-server + working_dir: /usr/src/app volumes: - - .:/usr/src/app # Mounts the current directory to /usr/src/app in the container + - /mnt/apps/DEV/REPOS/git.d-popov.com/ai-kevin:/usr/src/app + command: > + sh -c "npm install && node web/chat-server.js" environment: - NODE_ENV: demo # Sets the environment variable NODE_ENV to development - command: npm run start:demo-chat # Runs npm start when the container starts + NODE_ENV: demo + #TTS_BACKEND_URL: https://api.tts.d-popov.com/asr + TTS_API_URL: http://192.168.0.11:9009/asr + WS_URL: wss://ws.tts.d-popov.com + SERVER_PORT_HTTP: 8080 + SERVER_PORT_WS: 8082 + ports: + - 28080:8080 + - 28081:8082 + dns: + - 8.8.8.8 + - 8.8.4.4 \ No newline at end of file diff --git a/web/chat-server.js b/web/chat-server.js index 8cf4449..1e9c3e1 100644 --- a/web/chat-server.js +++ b/web/chat-server.js @@ -1,403 +1,479 @@ -const express = require('express'); -const bodyParser = require('body-parser'); -const WebSocket = require('ws'); -const storage = require('node-persist'); -const request = require('request'); -const fs = require('fs'); -const path = require('path'); -const dotenv = require('dotenv'); -const ollama = require('ollama'); -const axios = require('axios'); -const OpenAI = require('openai'); -const Groq = require('groq-sdk'); +const express = require('express') +const bodyParser = require('body-parser') +const WebSocket = require('ws') +const storage = require('node-persist') +const request = require('request') +const fs = require('fs') +const path = require('path') +const dotenv = require('dotenv') +const ollama = require('ollama') +const axios = require('axios') +const OpenAI = require('openai') +const Groq = require('groq-sdk') // Load environment variables -dotenv.config({ path: `.env${process.env.NODE_ENV === 'development' ? '.development' :'.'+ process.env.NODE_ENV }` }); +dotenv.config({ + path: `.env${ + process.env.NODE_ENV === 'development' + ? '.development' + : '.' + process.env.NODE_ENV + }` +}) +console.log(`loaded env file: ${process.env.NODE_ENV}`) // Initialize services -const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); -const groq = new Groq({ apiKey: process.env.GROQ_API_KEY }); +const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }) +const groq = new Groq({ apiKey: process.env.GROQ_API_KEY }) // Express setup -const app = express(); -app.use(bodyParser.json()); +const app = express() +app.use(bodyParser.json()) // Configuration constants -const PORT_HTTP = process.env.SERVER_PORT_HTTP || 3000; -const PORT_WS = process.env.SERVER_PORT_WS || 8080; -const TTS_API_URL = process.env.TTS_API_URL; -const LNN_API_URL = process.env.LNN_API_URL; -const LLN_MODEL = process.env.LLN_MODEL; +const PORT_HTTP = process.env.SERVER_PORT_HTTP || 3000 +const PORT_WS = process.env.SERVER_PORT_WS || 8082 +const TTS_API_URL = process.env.TTS_API_URL +const LNN_API_URL = process.env.LNN_API_URL +const LLN_MODEL = process.env.LLN_MODEL +console.log(`TTS API URL: ${TTS_API_URL}`) -let language = "en"; -let storeRecordings = false; -let queueCounter = 0; +let language = 'en' +let storeRecordings = false +let queueCounter = 0 -const sessions = new Map(); -const chats = new Map(); // Store chat rooms +const sessions = new Map() +const chats = new Map() // Store chat rooms // Initialize storage and load initial values -async function initStorage() { - await storage.init(); - language = await storage.getItem('language') || language; - storeRecordings = await storage.getItem('storeRecordings') || storeRecordings; +async function initStorage () { + await storage.init() + language = (await storage.getItem('language')) || language + storeRecordings = + (await storage.getItem('storeRecordings')) || storeRecordings - const storedChats = await storage.getItem('chats') || []; - storedChats.forEach(chat => chats.set(chat.id, chat)); + const storedChats = (await storage.getItem('chats')) || [] + storedChats.forEach(chat => chats.set(chat.id, chat)) - const storedSessions = await storage.getItem('sessions') || []; - storedSessions.forEach(session => sessions.set(session.sessionId, session)); + const storedSessions = (await storage.getItem('sessions')) || [] + storedSessions.forEach(session => sessions.set(session.sessionId, session)) } -initStorage(); +initStorage() // WebSocket Server -const wss = new WebSocket.Server({ port: PORT_WS }); +const wss = new WebSocket.Server({ port: PORT_WS }) wss.on('connection', ws => { - ws.on('message', async message => handleMessage(ws, message)); - ws.on('close', () => handleClose(ws)); -}); + ws.on('message', async message => handleMessage(ws, message)) + ws.on('close', () => handleClose(ws)) +}) // Handle WebSocket messages -async function handleMessage(ws, message) { - let data; - try { - data = JSON.parse(message); - } catch { - return handleAudioData(ws, message); - } +async function handleMessage (ws, message) { + let data + try { + data = JSON.parse(message) + } catch { + return handleAudioData(ws, message) + } - try { - switch (data.type) { - case 'sessionId': - await handleSessionId(ws); - break; - case 'join': - await handleJoin(ws, data); - break; - case 'startChat': - await handleStartChat(ws, data); - break; - case 'enterChat': - await handleEnterChat(ws, data); - break; - case 'reconnect': - await handleReconnect(ws, data); - break; - default: - console.log('Unknown message type:', data.type); - } - } catch (err) { - console.error('Failed to handle message', err); + try { + switch (data.type) { + case 'sessionId': + await handleSessionId(ws) + break + case 'join': + await handleJoin(ws, data) + break + case 'startChat': + await handleStartChat(ws, data) + break + case 'enterChat': + await handleEnterChat(ws, data) + break + case 'reconnect': + await handleReconnect(ws, data) + break + default: + console.log('Unknown message type:', data.type) } + } catch (err) { + console.error('Failed to handle message', err) + } } -function handleClose(ws) { - sessions.delete(ws.sessionId); - broadcastUserList(); +function handleClose (ws) { + sessions.delete(ws.sessionId) + broadcastUserList() } // Handlers for specific message types -async function handleSessionId(ws) { - ws.sessionId = generateSessionId(); - sessions.set(ws.sessionId, { language: 'en' }); - await storage.setItem('sessions', Array.from(sessions.values())); +async function handleSessionId (ws) { + ws.sessionId = generateSessionId() + sessions.set(ws.sessionId, { language: 'en' }) + await storage.setItem('sessions', Array.from(sessions.values())) } -async function handleJoin(ws, { username, language }) { - sessions.set(ws.sessionId, { username, sessionId: ws.sessionId, language }); - ws.send(JSON.stringify({ type: 'sessionId', sessionId: ws.sessionId, language, storeRecordings })); +async function handleJoin (ws, { username, language }) { + sessions.set(ws.sessionId, { username, sessionId: ws.sessionId, language }) + ws.send( + JSON.stringify({ + type: 'sessionId', + sessionId: ws.sessionId, + language, + storeRecordings + }) + ) - const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(ws.sessionId)); - ws.send(JSON.stringify({ type: 'chats', chats: userChats })); + const userChats = Array.from(chats.values()).filter(chat => + chat.participants.includes(ws.sessionId) + ) + ws.send(JSON.stringify({ type: 'chats', chats: userChats })) - broadcastUserList(); + broadcastUserList() } -async function handleStartChat(ws, { users }) { - const chatId = generateChatId(); - let participants = [ws.sessionId, ...users]; - participants = [...new Set(participants)]; +async function handleStartChat (ws, { users }) { + const chatId = generateChatId() + let participants = [ws.sessionId, ...users] + participants = [...new Set(participants)] - chats.set(chatId, { participants, messages: [] }); - await storage.setItem('chats', Array.from(chats.values())); + chats.set(chatId, { participants, messages: [] }) + await storage.setItem('chats', Array.from(chats.values())) - notifyParticipants(participants); - broadcastUserList(); + notifyParticipants(participants) + broadcastUserList() } -async function handleEnterChat(ws, { chatId }) { - const enteredChat = chats.get(chatId); - const currentSession = sessions.get(ws.sessionId); - currentSession.currentChat = chatId; - if (enteredChat && enteredChat.participants.includes(ws.sessionId)) { - ws.send(JSON.stringify({ type: 'chat', chat: enteredChat })); - } +async function handleEnterChat (ws, { chatId }) { + const enteredChat = chats.get(chatId) + const currentSession = sessions.get(ws.sessionId) + currentSession.currentChat = chatId + if (enteredChat && enteredChat.participants.includes(ws.sessionId)) { + ws.send(JSON.stringify({ type: 'chat', chat: enteredChat })) + } } -async function handleReconnect(ws, { sessionId }) { - const userSession = sessions.get(sessionId); - if (userSession) { - sessions.set(ws.sessionId, userSession); - ws.sessionId = sessionId; - const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(ws.sessionId)); - ws.send(JSON.stringify({ type: 'chats', chats: userChats })); - } else { - console.log('Session not found:', sessionId); - } - broadcastUserList(); +async function handleReconnect (ws, { sessionId }) { + const userSession = sessions.get(sessionId) + if (userSession) { + sessions.set(ws.sessionId, userSession) + ws.sessionId = sessionId + const userChats = Array.from(chats.values()).filter(chat => + chat.participants.includes(ws.sessionId) + ) + ws.send(JSON.stringify({ type: 'chats', chats: userChats })) + } else { + console.log('Session not found:', sessionId) + } + broadcastUserList() } // Utility functions -function generateSessionId() { - return Math.random().toString(36).substring(2); +function generateSessionId () { + return Math.random().toString(36).substring(2) } -function generateChatId() { - return Math.random().toString(36).substring(2); +function generateChatId () { + return Math.random().toString(36).substring(2) } -function broadcastUserList() { - const userList = Array.from(sessions.values()).map(user => ({ - username: user.username, - sessionId: user.sessionId, - currentChat: user.currentChat, - language: user.language - })); +function broadcastUserList () { + const userList = Array.from(sessions.values()).map(user => ({ + username: user.username, + sessionId: user.sessionId, + currentChat: user.currentChat, + language: user.language + })) - wss.clients.forEach(client => { - if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify({ type: 'userList', users: userList })); - } - }); -} - -function notifyParticipants(participants) { - participants.forEach(sessionId => { - const participantSocket = Array.from(wss.clients).find(client => client.sessionId === sessionId); - if (participantSocket && participantSocket.readyState === WebSocket.OPEN) { - const userChats = Array.from(chats.entries()) - .filter(([id, chat]) => chat.participants.includes(sessionId)) - .map(([id, chat]) => ({ id, participants: chat.participants })); - participantSocket.send(JSON.stringify({ type: 'chats', chats: userChats })); - } - }); -} - -async function handleAudioData(ws, data) { - const sessionData = sessions.get(ws.sessionId); - let { language, task } = sessionData; - - const formData = { - task: task || 'transcribe', - language, - vad_filter: 'true', - output: 'json', - audio_file: { - value: data, - options: { filename: 'audio.ogg', contentType: 'audio/ogg' } - } - }; - - if (!language || language === 'auto') { - await detectLanguage(ws, formData); - } else { - await transcribeAudio(ws, formData, sessionData); + wss.clients.forEach(client => { + if (client.readyState === WebSocket.OPEN) { + client.send(JSON.stringify({ type: 'userList', users: userList })) } + }) } -async function detectLanguage(ws, formData) { - try { - const result = await requestPromise({ - method: 'POST', - url: TTS_API_URL.replace('/asr', '/detect-language'), - formData - }); - const { language_code } = JSON.parse(result); - if (language_code) { - const sessionData = sessions.get(ws.sessionId); - sessionData.language = language_code; - ws.send(JSON.stringify({ type: 'languageDetected', languageDetected: language_code })); - await transcribeAudio(ws, formData, sessionData); - } - } catch (err) { - console.error('Language detection failed:', err); +function notifyParticipants (participants) { + participants.forEach(sessionId => { + const participantSocket = Array.from(wss.clients).find( + client => client.sessionId === sessionId + ) + if (participantSocket && participantSocket.readyState === WebSocket.OPEN) { + const userChats = Array.from(chats.entries()) + .filter(([id, chat]) => chat.participants.includes(sessionId)) + .map(([id, chat]) => ({ id, participants: chat.participants })) + participantSocket.send( + JSON.stringify({ type: 'chats', chats: userChats }) + ) } + }) } -async function transcribeAudio(ws, formData, sessionData) { - const start = new Date().getTime(); - queueCounter++; +async function handleAudioData (ws, data) { + const sessionData = sessions.get(ws.sessionId) + let { language, task } = sessionData - try { - if(sessionData.language) { - formData.language = sessionData.language; - } - formData.vad_filter = 'true'; - const body = await requestPromise({ method: 'POST', url: TTS_API_URL, formData }); - queueCounter--; - - const duration = new Date().getTime() - start; - ws.send(JSON.stringify({ - type: 'text', - queueCounter, - duration, - language: sessionData.language, - text: body - })); - - await handleChatTranscription(ws, body, sessionData); - - } catch (err) { - console.error('Transcription failed:', err); + const formData = { + task: task || 'transcribe', + language, + vad_filter: 'true', + output: 'json', + audio_file: { + value: data, + options: { filename: 'audio.ogg', contentType: 'audio/ogg' } } + } - if (storeRecordings) { - const timestamp = Date.now(); - fs.mkdir('rec', { recursive: true }, err => { - if (err) console.error(err); - else { - fs.writeFile(`rec/audio${timestamp}.ogg`, formData.audio_file.value, err => { - if (err) console.error(err); - else console.log(`Audio data saved to rec/audio${timestamp}.ogg`); - }); + if (!language || language === 'auto') { + await detectLanguage(ws, formData) + } else { + await transcribeAudio(ws, formData, sessionData) + } +} + +async function detectLanguage (ws, formData) { + try { + const result = await requestPromise({ + method: 'POST', + url: TTS_API_URL.replace('/asr', '/detect-language'), + formData + }) + const { language_code } = JSON.parse(result) + if (language_code) { + const sessionData = sessions.get(ws.sessionId) + sessionData.language = language_code + ws.send( + JSON.stringify({ + type: 'languageDetected', + languageDetected: language_code + }) + ) + await transcribeAudio(ws, formData, sessionData) + } + } catch (err) { + console.error('Language detection failed:', err) + } +} + +async function transcribeAudio (ws, formData, sessionData) { + const start = new Date().getTime() + queueCounter++ + + try { + if (sessionData.language) { + formData.language = sessionData.language + } + formData.vad_filter = 'true' + const body = await requestPromise({ + method: 'POST', + url: TTS_API_URL, + formData + }) + queueCounter-- + + const duration = new Date().getTime() - start + ws.send( + JSON.stringify({ + type: 'text', + queueCounter, + duration, + language: sessionData.language, + text: body + }) + ) + + await handleChatTranscription(ws, body, sessionData) + } catch (err) { + console.error('Transcription failed:', err) + } + + if (storeRecordings) { + const timestamp = Date.now() + fs.mkdir('rec', { recursive: true }, err => { + if (err) console.error(err) + else { + fs.writeFile( + `rec/audio${timestamp}.ogg`, + formData.audio_file.value, + err => { + if (err) console.error(err) + else console.log(`Audio data saved to rec/audio${timestamp}.ogg`) + } + ) + } + }) + } +} + +async function handleChatTranscription (ws, body, sessionData) { + if (sessionData.currentChat) { + const chat = chats.get(sessionData.currentChat) + if (chat) { + let msg = { sender: sessionData.username, text: body, translations: [] } + chat.messages.push(msg) + + for (let sessionId of chat.participants) { + if (sessionId !== ws.sessionId) { + const targetLang = sessions.get(sessionId)?.language || 'en' + if (targetLang !== sessionData.language) { + const translation = await translateText( + body, + sessionData.language, + targetLang + ) + msg.translations.push({ language: targetLang, text: translation }) + + const participantSocket = Array.from(wss.clients).find( + client => client.sessionId === sessionId + ) + if ( + participantSocket && + participantSocket.readyState === WebSocket.OPEN + ) { + participantSocket.send( + JSON.stringify({ + type: 'text', + text: `${sessionData.username}: ${translation}` + }) + ) + const audioBuffer = await generateSpeech(translation) + participantSocket.send( + JSON.stringify({ + type: 'audio', + audio: audioBuffer.toString('base64') + }) + ) } - }); - } -} - -async function handleChatTranscription(ws, body, sessionData) { - if (sessionData.currentChat) { - const chat = chats.get(sessionData.currentChat); - if (chat) { - let msg = { sender: sessionData.username, text: body, translations: [] }; - chat.messages.push(msg); - - for (let sessionId of chat.participants) { - if (sessionId !== ws.sessionId) { - const targetLang = sessions.get(sessionId)?.language || 'en'; - if (targetLang !== sessionData.language) { - const translation = await translateText(body, sessionData.language, targetLang); - msg.translations.push({ language: targetLang, text: translation }); - - const participantSocket = Array.from(wss.clients).find(client => client.sessionId === sessionId); - if (participantSocket && participantSocket.readyState === WebSocket.OPEN) { - participantSocket.send(JSON.stringify({ type: 'text', text: `${sessionData.username}: ${translation}` })); - const audioBuffer = await generateSpeech(translation); - participantSocket.send(JSON.stringify({ type: 'audio', audio: audioBuffer.toString('base64') })); - } - } else { - const participantSocket = Array.from(wss.clients).find(client => client.sessionId === sessionId); - if (participantSocket && participantSocket.readyState === WebSocket.OPEN) { - participantSocket.send(JSON.stringify({ type: 'text', text: `${sessionData.username}: ${body}` })); - participantSocket.send(JSON.stringify({ type: 'audio', audio: formData.toString('base64') })); - } - } - } + } else { + const participantSocket = Array.from(wss.clients).find( + client => client.sessionId === sessionId + ) + if ( + participantSocket && + participantSocket.readyState === WebSocket.OPEN + ) { + participantSocket.send( + JSON.stringify({ + type: 'text', + text: `${sessionData.username}: ${body}` + }) + ) + participantSocket.send( + JSON.stringify({ + type: 'audio', + audio: formData.toString('base64') + }) + ) } + } } + } } + } } -async function translateText(originalText, originalLanguage, targetLanguage) { - const prompt = `Translate this text from ${originalLanguage} to ${targetLanguage}: ${originalText}`; +async function translateText (originalText, originalLanguage, targetLanguage) { + const prompt = `Translate this text from ${originalLanguage} to ${targetLanguage}: ${originalText}` - const response = await groq.chat.completions.create({ - messages: [ - { - role: "system", - content: `You are translating voice transcriptions from '${originalLanguage}' to '${targetLanguage}'. Reply with just the translation.`, - }, - { - role: "user", - content: originalText, - }, - ], - model: "llama3-8b-8192", - }); + const response = await groq.chat.completions.create({ + messages: [ + { + role: 'system', + content: `You are translating voice transcriptions from '${originalLanguage}' to '${targetLanguage}'. Reply with just the translation.` + }, + { + role: 'user', + content: originalText + } + ], + model: 'llama3-8b-8192' + }) - return response.choices[0]?.message?.content || ""; + return response.choices[0]?.message?.content || '' } -async function generateSpeech(text) { - const mp3 = await openai.audio.speech.create({ - model: "tts-1", - voice: "alloy", - input: text, - }); - return Buffer.from(await mp3.arrayBuffer()); +async function generateSpeech (text) { + const mp3 = await openai.audio.speech.create({ + model: 'tts-1', + voice: 'alloy', + input: text + }) + return Buffer.from(await mp3.arrayBuffer()) } // HTTP Server app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'chat-client.html')); -}); + res.sendFile(path.join(__dirname, 'chat-client.html')) +}) app.get('/audio.js', (req, res) => { - res.sendFile(path.join(__dirname, 'audio.js')); -}); + res.sendFile(path.join(__dirname, 'audio.js')) +}) app.post('/log', (req, res) => { - console.log(`[LOG ${new Date().toISOString()}] ${req.body.message}`); - res.status(200).send('OK'); -}); + console.log(`[LOG ${new Date().toISOString()}] ${req.body.message}`) + res.status(200).send('OK') +}) app.get('/wsurl', (req, res) => { - // if(process.env.PUBLIC_HOSTNAME){ - // process.env.WS_URL = `wss://${process.env.PUBLIC_HOSTNAME}` - // } - console.log('Request for WS URL resolved with:', process.env.PUBLIC_WS_URL ); - res.status(200).send(process.env.PUBLIC_WS_URL); -}); + // if(process.env.PUBLIC_HOSTNAME){ + // process.env.WS_URL = `wss://${process.env.PUBLIC_HOSTNAME}` + // } + console.log('Request for WS URL resolved with:', process.env.WS_URL) + res.status(200).send(process.env.WS_URL) +}) app.get('/settings', async (req, res) => { - if (req.query.language) { - language = req.query.language; - await storage.setItem('language', language); - } - if (req.query.storeRecordings) { - storeRecordings = req.query.storeRecordings; - await storage.setItem('storeRecordings', storeRecordings); - } - res.status(200).send({ language, storeRecordings }); -}); + if (req.query.language) { + language = req.query.language + await storage.setItem('language', language) + } + if (req.query.storeRecordings) { + storeRecordings = req.query.storeRecordings + await storage.setItem('storeRecordings', storeRecordings) + } + res.status(200).send({ language, storeRecordings }) +}) app.post('/settings', async (req, res) => { - const { sessionId, language, storeRecordings, task } = req.body; - const sessionData = sessions.get(sessionId); - if (language) sessionData.language = language; - if (storeRecordings !== undefined) sessionData.storeRecordings = storeRecordings; - if (task) sessionData.task = task; - res.status(200).send('OK'); -}); + const { sessionId, language, storeRecordings, task } = req.body + const sessionData = sessions.get(sessionId) + if (language) sessionData.language = language + if (storeRecordings !== undefined) + sessionData.storeRecordings = storeRecordings + if (task) sessionData.task = task + res.status(200).send('OK') +}) app.post('/upload', (req, res) => { - const timestamp = Date.now(); - console.log('Received audio data:', timestamp); - fs.mkdir('rec', { recursive: true }, err => { - if (err) return res.status(500).send('ERROR'); - const file = fs.createWriteStream(`rec/audio_slice_${timestamp}.ogg`); - req.pipe(file); - file.on('finish', () => res.status(200).send('OK')); - }); -}); + const timestamp = Date.now() + console.log('Received audio data:', timestamp) + fs.mkdir('rec', { recursive: true }, err => { + if (err) return res.status(500).send('ERROR') + const file = fs.createWriteStream(`rec/audio_slice_${timestamp}.ogg`) + req.pipe(file) + file.on('finish', () => res.status(200).send('OK')) + }) +}) app.get('/chats', (req, res) => { - const { username } = req.query; - const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(username)); - res.status(200).send({ chats: userChats }); -}); + const { username } = req.query + const userChats = Array.from(chats.values()).filter(chat => + chat.participants.includes(username) + ) + res.status(200).send({ chats: userChats }) +}) app.listen(PORT_HTTP, () => { - console.log(`Server listening on port ${PORT_HTTP}`); -}); + console.log(`Server listening on port ${PORT_HTTP}`) + console.log(process.env.TTS_BACKEND_URL) +}) // Helper to wrap request in a promise -function requestPromise(options) { - return new Promise((resolve, reject) => { - request(options, (error, response, body) => { - if (error) return reject(error); - resolve(body); - }); - }); +function requestPromise (options) { + return new Promise((resolve, reject) => { + request(options, (error, response, body) => { + if (error) return reject(error) + resolve(body) + }) + }) } From 06786a4a6e632bf06cd7d61df3e391a12b1b714c Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Wed, 2 Oct 2024 14:21:10 +0300 Subject: [PATCH 3/7] kevin crypto follow implementation --- crypto/sol/app.py | 68 ++++++++++++++++++++++++++++++++++++++++++-- crypto/sol/readme.md | 13 +++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 crypto/sol/readme.md diff --git a/crypto/sol/app.py b/crypto/sol/app.py index f334c9d..c95463c 100644 --- a/crypto/sol/app.py +++ b/crypto/sol/app.py @@ -1,8 +1,24 @@ from flask import Flask, render_template, request, jsonify from solana.rpc.api import Client +from dexscreener import DexscreenerClient +import requests +import os app = Flask(__name__) solana_client = Client("https://api.mainnet-beta.solana.com") +dexscreener_client = DexscreenerClient() + +# Replace with the wallet address you want to follow +FOLLOWED_WALLET = "REPLACE_WITH_WALLET_ADDRESS" + +# Replace with your wallet address +YOUR_WALLET = "REPLACE_WITH_YOUR_WALLET_ADDRESS" + +# Simulated wallet balances (replace with actual balance fetching logic) +wallet_balances = { + FOLLOWED_WALLET: {"SOL": 100, "USDC": 1000}, + YOUR_WALLET: {"SOL": 10, "USDC": 100} +} @app.route('/') def index(): @@ -10,8 +26,8 @@ def index(): @app.route('/tokens', methods=['GET']) def get_tokens(): - # Here you would add logic to fetch new tokens or token data - return jsonify(['SOL', 'USDC']) # Example token list + # Fetch tokens from your wallet + return jsonify(list(wallet_balances[YOUR_WALLET].keys())) @app.route('/swap', methods=['POST']) def swap_tokens(): @@ -20,6 +36,52 @@ def swap_tokens(): amount = data['amount'] # Here you would add logic to perform the token swap return jsonify({'status': 'success', 'message': f'Swapped {amount} of {token_name}'}) + +@app.route('/balances', methods=['GET']) +def get_balances(): + return jsonify(wallet_balances[YOUR_WALLET]) + +@app.route('/followed_wallet_moves', methods=['GET']) +def get_followed_wallet_moves(): + # In a real-world scenario, you'd use a blockchain explorer API to get recent transactions + # For this example, we'll simulate a move + simulated_move = { + "token": "SOL", + "action": "swap", + "amount": 5, + "to_token": "USDC" + } + return jsonify(simulated_move) + +@app.route('/follow_move', methods=['POST']) +def follow_move(): + move = request.json + followed_balance = wallet_balances[FOLLOWED_WALLET][move['token']] + your_balance = wallet_balances[YOUR_WALLET][move['token']] + + proportion = your_balance / followed_balance + amount_to_swap = move['amount'] * proportion + + # Perform the swap (simulated) + if wallet_balances[YOUR_WALLET][move['token']] >= amount_to_swap: + wallet_balances[YOUR_WALLET][move['token']] -= amount_to_swap + + # Get the current price of the token pair + pair = dexscreener_client.get_token_pair("solana", move['token']) + price = float(pair['priceUsd']) + + received_amount = amount_to_swap * price + wallet_balances[YOUR_WALLET][move['to_token']] += received_amount + + return jsonify({ + 'status': 'success', + 'message': f"Swapped {amount_to_swap} {move['token']} for {received_amount} {move['to_token']}" + }) + else: + return jsonify({ + 'status': 'error', + 'message': f"Insufficient balance to swap {amount_to_swap} {move['token']}" + }) if __name__ == '__main__': - app.run(debug=True) + app.run(debug=True) \ No newline at end of file diff --git a/crypto/sol/readme.md b/crypto/sol/readme.md new file mode 100644 index 0000000..b864354 --- /dev/null +++ b/crypto/sol/readme.md @@ -0,0 +1,13 @@ +o run this Python Solana agent: + +Install the required libraries: + +~pip install flask solana dexscreener~ + +Replace REPLACE_WITH_WALLET_ADDRESS with the wallet address you want to follow. +Replace REPLACE_WITH_YOUR_WALLET_ADDRESS with your own wallet address. +Save the code in a file, e.g., solana_agent.py. + +Run the Flask application: + +~python solana_agent.py~ \ No newline at end of file From d1cfdb46e8248715ea68382ab5988e9b684bd91d Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Wed, 2 Oct 2024 14:21:19 +0300 Subject: [PATCH 4/7] misc --- web/chat-client.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/chat-client.html b/web/chat-client.html index d51d6a4..eaace96 100644 --- a/web/chat-client.html +++ b/web/chat-client.html @@ -290,7 +290,7 @@ showClearSessionOption(); connect().then(() => { - // audio.initializeVolumeChecker(); + initializeVolumeChecker(); }); }; @@ -550,7 +550,7 @@ export function initializeVolumeChecker() { volumeChecker = setInterval(() => { if (!audioContext) { - console.log("No audio context"); + //console.log("No audio context"); return; } const frequencyData = new Uint8Array(analyser.frequencyBinCount); From 382c9ae085b672bc5b4ea0035227acea8847a453 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Wed, 2 Oct 2024 14:31:21 +0300 Subject: [PATCH 5/7] crypto bot follow added telegram logs --- crypto/sol/app.py | 72 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/crypto/sol/app.py b/crypto/sol/app.py index c95463c..3160d7a 100644 --- a/crypto/sol/app.py +++ b/crypto/sol/app.py @@ -1,18 +1,45 @@ from flask import Flask, render_template, request, jsonify from solana.rpc.api import Client from dexscreener import DexscreenerClient -import requests +import asyncio +from telegram import Bot +from telegram.constants import ParseMode import os app = Flask(__name__) solana_client = Client("https://api.mainnet-beta.solana.com") dexscreener_client = DexscreenerClient() + + + + + + +# This can be your own ID, or one for a developer group/channel. +# You can use the /start command of this bot to see your chat id. +DEVELOPER_CHAT_ID = "777826553" + + # Replace with the wallet address you want to follow -FOLLOWED_WALLET = "REPLACE_WITH_WALLET_ADDRESS" +FOLLOWED_WALLET = "9U7D916zuQ8qcL9kQZqkcroWhHGho5vD8VNekvztrutN" # Replace with your wallet address -YOUR_WALLET = "REPLACE_WITH_YOUR_WALLET_ADDRESS" +YOUR_WALLET = "65nzyZXTLC81MthTo52a2gRJjqryTizWVqpK2fDKLye5" + + + +TELEGRAM_CHAT_ID = "777826553" +# Replace with your Telegram Bot Token +TELEGRAM_BOT_TOKEN = "6749075936:AAHUHiPTDEIu6JH7S2fQdibwsu6JVG3FNG0" + +# Telegram Bot Token +# t.me/kevin_ai_robot +# TOKEN = '6805059978:AAHNJKuOeazMSJHc3-BXRCsFfEVyFHeFnjw' +# t.me/artitherobot 6749075936:AAHUHiPTDEIu6JH7S2fQdibwsu6JVG3FNG0 + +# Initialize Telegram Bot +bot = Bot(token=TELEGRAM_BOT_TOKEN) # Simulated wallet balances (replace with actual balance fetching logic) wallet_balances = { @@ -20,23 +47,17 @@ wallet_balances = { YOUR_WALLET: {"SOL": 10, "USDC": 100} } +async def send_telegram_message(message): + await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message, parse_mode=ParseMode.HTML) + @app.route('/') def index(): return render_template('index.html') @app.route('/tokens', methods=['GET']) def get_tokens(): - # Fetch tokens from your wallet return jsonify(list(wallet_balances[YOUR_WALLET].keys())) -@app.route('/swap', methods=['POST']) -def swap_tokens(): - data = request.json - token_name = data['token_name'] - amount = data['amount'] - # Here you would add logic to perform the token swap - return jsonify({'status': 'success', 'message': f'Swapped {amount} of {token_name}'}) - @app.route('/balances', methods=['GET']) def get_balances(): return jsonify(wallet_balances[YOUR_WALLET]) @@ -51,6 +72,14 @@ def get_followed_wallet_moves(): "amount": 5, "to_token": "USDC" } + + # Send Telegram notification about the detected move + asyncio.run(send_telegram_message( + f"Move Detected:\n" + f"Followed wallet swapped {simulated_move['amount']} {simulated_move['token']} " + f"to {simulated_move['to_token']}" + )) + return jsonify(simulated_move) @app.route('/follow_move', methods=['POST']) @@ -62,25 +91,36 @@ def follow_move(): proportion = your_balance / followed_balance amount_to_swap = move['amount'] * proportion - # Perform the swap (simulated) if wallet_balances[YOUR_WALLET][move['token']] >= amount_to_swap: wallet_balances[YOUR_WALLET][move['token']] -= amount_to_swap - # Get the current price of the token pair pair = dexscreener_client.get_token_pair("solana", move['token']) price = float(pair['priceUsd']) received_amount = amount_to_swap * price wallet_balances[YOUR_WALLET][move['to_token']] += received_amount + # Send Telegram notification about the followed move + asyncio.run(send_telegram_message( + f"Move Followed:\n" + f"Swapped {amount_to_swap:.4f} {move['token']} " + f"for {received_amount:.4f} {move['to_token']}" + )) + return jsonify({ 'status': 'success', - 'message': f"Swapped {amount_to_swap} {move['token']} for {received_amount} {move['to_token']}" + 'message': f"Swapped {amount_to_swap:.4f} {move['token']} for {received_amount:.4f} {move['to_token']}" }) else: + # Send Telegram notification about the failed move + asyncio.run(send_telegram_message( + f"Move Failed:\n" + f"Insufficient balance to swap {amount_to_swap:.4f} {move['token']}" + )) + return jsonify({ 'status': 'error', - 'message': f"Insufficient balance to swap {amount_to_swap} {move['token']}" + 'message': f"Insufficient balance to swap {amount_to_swap:.4f} {move['token']}" }) if __name__ == '__main__': From 3fcbff27dd8b28efc5cfdb9a540cea78f7572488 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Wed, 2 Oct 2024 14:57:10 +0300 Subject: [PATCH 6/7] get wallet ballances --- crypto/sol/app.py | 160 +++++++++++++++++++++++++++++++++++-------- crypto/sol/readme.md | 7 +- 2 files changed, 134 insertions(+), 33 deletions(-) diff --git a/crypto/sol/app.py b/crypto/sol/app.py index 3160d7a..33cd3fa 100644 --- a/crypto/sol/app.py +++ b/crypto/sol/app.py @@ -1,66 +1,102 @@ + from flask import Flask, render_template, request, jsonify -from solana.rpc.api import Client +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Confirmed +from solders.pubkey import Pubkey from dexscreener import DexscreenerClient import asyncio from telegram import Bot from telegram.constants import ParseMode import os +import datetime app = Flask(__name__) -solana_client = Client("https://api.mainnet-beta.solana.com") + +solana_client = AsyncClient("https://api.mainnet-beta.solana.com") dexscreener_client = DexscreenerClient() - - - - - # This can be your own ID, or one for a developer group/channel. # You can use the /start command of this bot to see your chat id. DEVELOPER_CHAT_ID = "777826553" - - # Replace with the wallet address you want to follow FOLLOWED_WALLET = "9U7D916zuQ8qcL9kQZqkcroWhHGho5vD8VNekvztrutN" - # Replace with your wallet address YOUR_WALLET = "65nzyZXTLC81MthTo52a2gRJjqryTizWVqpK2fDKLye5" - - -TELEGRAM_CHAT_ID = "777826553" -# Replace with your Telegram Bot Token -TELEGRAM_BOT_TOKEN = "6749075936:AAHUHiPTDEIu6JH7S2fQdibwsu6JVG3FNG0" - +# Channel DBot: +# TELEGRAM_CHAT_ID = "777826553" # Telegram Bot Token # t.me/kevin_ai_robot # TOKEN = '6805059978:AAHNJKuOeazMSJHc3-BXRCsFfEVyFHeFnjw' # t.me/artitherobot 6749075936:AAHUHiPTDEIu6JH7S2fQdibwsu6JVG3FNG0 +# Replace with your Telegram Bot Token +TELEGRAM_BOT_TOKEN = "6805059978:AAHNJKuOeazMSJHc3-BXRCsFfEVyFHeFnjw" + + # Initialize Telegram Bot bot = Bot(token=TELEGRAM_BOT_TOKEN) - -# Simulated wallet balances (replace with actual balance fetching logic) -wallet_balances = { - FOLLOWED_WALLET: {"SOL": 100, "USDC": 1000}, - YOUR_WALLET: {"SOL": 10, "USDC": 100} -} - -async def send_telegram_message(message): - await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message, parse_mode=ParseMode.HTML) +t send_telegram_message(message) @app.route('/') def index(): return render_template('index.html') @app.route('/tokens', methods=['GET']) -def get_tokens(): - return jsonify(list(wallet_balances[YOUR_WALLET].keys())) +async def get_tokens(): + balances = await get_wallet_balances(YOUR_WALLET) + return jsonify(list(balances.keys())) @app.route('/balances', methods=['GET']) -def get_balances(): - return jsonify(wallet_balances[YOUR_WALLET]) +async def get_balances(): + balances = await get_wallet_balances(YOUR_WALLET) + return jsonify(balances) + + +@app.route('/follow_move', methods=['POST']) +async def follow_move(): + move = request.json + followed_balances = await get_wallet_balances(FOLLOWED_WALLET) + your_balances = await get_wallet_balances(YOUR_WALLET) + + if move['token'] not in followed_balances or move['token'] not in your_balances: + return jsonify({'status': 'error', 'message': 'Invalid token'}) + + followed_balance = followed_balances[move['token']] + your_balance = your_balances[move['token']] + + proportion = your_balance / followed_balance if followed_balance > 0 else 0 + amount_to_swap = move['amount'] * proportion + + if your_balance >= amount_to_swap: + # Here you would implement the actual swap logic + # For now, we'll just simulate the swap + pair = dexscreener_client.get_token_pair("solana", move['token']) + price = float(pair['priceUsd']) + received_amount = amount_to_swap * price + + await send_telegram_message( + f"Move Followed:\n" + f"Swapped {amount_to_swap:.6f} {move['token']} " + f"for {received_amount:.6f} {move['to_token']}" + ) + + return jsonify({ + 'status': 'success', + 'message': f"Swapped {amount_to_swap:.6f} {move['token']} for {received_amount:.6f} {move['to_token']}" + }) + else: + await send_telegram_message( + f"Move Failed:\n" + f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}" + ) + + return jsonify({ + 'status': 'error', + 'message': f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}" + }) + @app.route('/followed_wallet_moves', methods=['GET']) def get_followed_wallet_moves(): @@ -123,5 +159,69 @@ def follow_move(): 'message': f"Insufficient balance to swap {amount_to_swap:.4f} {move['token']}" }) + +async def get_wallet_balances(wallet_address): + balances = {} + for token, address in TOKEN_ADDRESSES.items(): + balances[token] = await get_token_balance(wallet_address, address) + return balances + +async def send_telegram_message(message): + return + # await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message, parse_mode=ParseMode.HTML) + +async def get_non_zero_token_balances(wallet_address): + non_zero_balances = {} + for token, address in TOKEN_ADDRESSES.items(): + balance = await get_token_balance(wallet_address, address) + if balance > 0: + non_zero_balances[token] = address + return non_zero_balances + +async def list_initial_wallet_states(): + global TOKEN_ADDRESSES + + followed_wallet_balances = await get_wallet_balances(FOLLOWED_WALLET) + your_wallet_balances = await get_wallet_balances(YOUR_WALLET) + + followed_non_zero = await get_non_zero_token_balances(FOLLOWED_WALLET) + your_non_zero = await get_non_zero_token_balances(YOUR_WALLET) + + # Update TOKEN_ADDRESSES with non-zero balances from both wallets + TOKEN_ADDRESSES = {**followed_non_zero, **your_non_zero} + + followed_wallet_state = "\n".join([f"{token}: {amount:.6f}" for token, amount in followed_wallet_balances.items() if amount > 0]) + your_wallet_state = "\n".join([f"{token}: {amount:.6f}" for token, amount in your_wallet_balances.items() if amount > 0]) + + message = ( + "Initial Wallet States (Non-zero balances):\n\n" + f"Followed Wallet ({FOLLOWED_WALLET}):\n" + f"{followed_wallet_state}\n\n" + f"Your Wallet ({YOUR_WALLET}):\n" + f"{your_wallet_state}\n\n" + f"Monitored Tokens:\n" + f"{', '.join(TOKEN_ADDRESSES.keys())}" + ) + + await send_telegram_message(message) + + + + + +async def send_startup_message(): + current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + message = ( + f"Solana Agent Application Started\n\n" + f"Startup Time: {current_time}\n" + f"Followed Wallet: {FOLLOWED_WALLET}\n" + f"Your Wallet: {YOUR_WALLET}\n\n" + "The application is now running and ready to monitor wallet activities." + ) + await send_telegram_message(message) + if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file + asyncio.run(send_startup_message()) + asyncio.run(list_initial_wallet_states()) + print(f"Monitored Tokens: {', '.join(TOKEN_ADDRESSES.keys())}") + app.run(debug=True, port=3009) \ No newline at end of file diff --git a/crypto/sol/readme.md b/crypto/sol/readme.md index b864354..130732b 100644 --- a/crypto/sol/readme.md +++ b/crypto/sol/readme.md @@ -1,8 +1,9 @@ -o run this Python Solana agent: +`Conda activate trade` +To run this Python Solana agent: Install the required libraries: -~pip install flask solana dexscreener~ +`pip install flask solana dexscreener python-telegram-bot` Replace REPLACE_WITH_WALLET_ADDRESS with the wallet address you want to follow. Replace REPLACE_WITH_YOUR_WALLET_ADDRESS with your own wallet address. @@ -10,4 +11,4 @@ Save the code in a file, e.g., solana_agent.py. Run the Flask application: -~python solana_agent.py~ \ No newline at end of file +`python app.py` \ No newline at end of file From 9ed2f0eb6e669196b0a016a1b84f460dd54ee445 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Wed, 2 Oct 2024 14:57:59 +0300 Subject: [PATCH 7/7] cleanup --- crypto/sol/app.py | 118 ++++++++++------------------------------------ 1 file changed, 24 insertions(+), 94 deletions(-) diff --git a/crypto/sol/app.py b/crypto/sol/app.py index 33cd3fa..5c895de 100644 --- a/crypto/sol/app.py +++ b/crypto/sol/app.py @@ -1,4 +1,3 @@ - from flask import Flask, render_template, request, jsonify from solana.rpc.async_api import AsyncClient from solana.rpc.commitment import Confirmed @@ -7,37 +6,42 @@ from dexscreener import DexscreenerClient import asyncio from telegram import Bot from telegram.constants import ParseMode -import os import datetime app = Flask(__name__) +# Use the production Solana RPC endpoint solana_client = AsyncClient("https://api.mainnet-beta.solana.com") dexscreener_client = DexscreenerClient() - -# This can be your own ID, or one for a developer group/channel. -# You can use the /start command of this bot to see your chat id. +# Configuration DEVELOPER_CHAT_ID = "777826553" -# Replace with the wallet address you want to follow FOLLOWED_WALLET = "9U7D916zuQ8qcL9kQZqkcroWhHGho5vD8VNekvztrutN" -# Replace with your wallet address YOUR_WALLET = "65nzyZXTLC81MthTo52a2gRJjqryTizWVqpK2fDKLye5" - -# Channel DBot: -# TELEGRAM_CHAT_ID = "777826553" -# Telegram Bot Token -# t.me/kevin_ai_robot -# TOKEN = '6805059978:AAHNJKuOeazMSJHc3-BXRCsFfEVyFHeFnjw' -# t.me/artitherobot 6749075936:AAHUHiPTDEIu6JH7S2fQdibwsu6JVG3FNG0 -# Replace with your Telegram Bot Token TELEGRAM_BOT_TOKEN = "6805059978:AAHNJKuOeazMSJHc3-BXRCsFfEVyFHeFnjw" - - # Initialize Telegram Bot bot = Bot(token=TELEGRAM_BOT_TOKEN) -t send_telegram_message(message) + +# Token addresses (to be populated dynamically) +TOKEN_ADDRESSES = {} + +async def get_token_balance(wallet_address, token_address): + try: + balance = await solana_client.get_token_account_balance( + Pubkey.from_string(token_address), + commitment=Confirmed + ) + return float(balance['result']['value']['uiAmount']) + except Exception as e: + print(f"Error getting balance for {token_address}: {str(e)}") + return 0 + +async def send_telegram_message(message): + try: + await bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=message, parse_mode=ParseMode.HTML) + except Exception as e: + print(f"Error sending Telegram message: {str(e)}") @app.route('/') def index(): @@ -53,7 +57,6 @@ async def get_balances(): balances = await get_wallet_balances(YOUR_WALLET) return jsonify(balances) - @app.route('/follow_move', methods=['POST']) async def follow_move(): move = request.json @@ -70,8 +73,7 @@ async def follow_move(): amount_to_swap = move['amount'] * proportion if your_balance >= amount_to_swap: - # Here you would implement the actual swap logic - # For now, we'll just simulate the swap + # Implement actual swap logic here pair = dexscreener_client.get_token_pair("solana", move['token']) price = float(pair['priceUsd']) received_amount = amount_to_swap * price @@ -97,79 +99,12 @@ async def follow_move(): 'message': f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}" }) - -@app.route('/followed_wallet_moves', methods=['GET']) -def get_followed_wallet_moves(): - # In a real-world scenario, you'd use a blockchain explorer API to get recent transactions - # For this example, we'll simulate a move - simulated_move = { - "token": "SOL", - "action": "swap", - "amount": 5, - "to_token": "USDC" - } - - # Send Telegram notification about the detected move - asyncio.run(send_telegram_message( - f"Move Detected:\n" - f"Followed wallet swapped {simulated_move['amount']} {simulated_move['token']} " - f"to {simulated_move['to_token']}" - )) - - return jsonify(simulated_move) - -@app.route('/follow_move', methods=['POST']) -def follow_move(): - move = request.json - followed_balance = wallet_balances[FOLLOWED_WALLET][move['token']] - your_balance = wallet_balances[YOUR_WALLET][move['token']] - - proportion = your_balance / followed_balance - amount_to_swap = move['amount'] * proportion - - if wallet_balances[YOUR_WALLET][move['token']] >= amount_to_swap: - wallet_balances[YOUR_WALLET][move['token']] -= amount_to_swap - - pair = dexscreener_client.get_token_pair("solana", move['token']) - price = float(pair['priceUsd']) - - received_amount = amount_to_swap * price - wallet_balances[YOUR_WALLET][move['to_token']] += received_amount - - # Send Telegram notification about the followed move - asyncio.run(send_telegram_message( - f"Move Followed:\n" - f"Swapped {amount_to_swap:.4f} {move['token']} " - f"for {received_amount:.4f} {move['to_token']}" - )) - - return jsonify({ - 'status': 'success', - 'message': f"Swapped {amount_to_swap:.4f} {move['token']} for {received_amount:.4f} {move['to_token']}" - }) - else: - # Send Telegram notification about the failed move - asyncio.run(send_telegram_message( - f"Move Failed:\n" - f"Insufficient balance to swap {amount_to_swap:.4f} {move['token']}" - )) - - return jsonify({ - 'status': 'error', - 'message': f"Insufficient balance to swap {amount_to_swap:.4f} {move['token']}" - }) - - async def get_wallet_balances(wallet_address): balances = {} for token, address in TOKEN_ADDRESSES.items(): balances[token] = await get_token_balance(wallet_address, address) return balances -async def send_telegram_message(message): - return - # await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message, parse_mode=ParseMode.HTML) - async def get_non_zero_token_balances(wallet_address): non_zero_balances = {} for token, address in TOKEN_ADDRESSES.items(): @@ -187,7 +122,6 @@ async def list_initial_wallet_states(): followed_non_zero = await get_non_zero_token_balances(FOLLOWED_WALLET) your_non_zero = await get_non_zero_token_balances(YOUR_WALLET) - # Update TOKEN_ADDRESSES with non-zero balances from both wallets TOKEN_ADDRESSES = {**followed_non_zero, **your_non_zero} followed_wallet_state = "\n".join([f"{token}: {amount:.6f}" for token, amount in followed_wallet_balances.items() if amount > 0]) @@ -204,10 +138,6 @@ async def list_initial_wallet_states(): ) await send_telegram_message(message) - - - - async def send_startup_message(): current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") @@ -224,4 +154,4 @@ if __name__ == '__main__': asyncio.run(send_startup_message()) asyncio.run(list_initial_wallet_states()) print(f"Monitored Tokens: {', '.join(TOKEN_ADDRESSES.keys())}") - app.run(debug=True, port=3009) \ No newline at end of file + app.run(host='0.0.0.0', port=3009) \ No newline at end of file