Skip to content

Commit

Permalink
Merge pull request #335 from CommandDash/web-app-ui-fixes
Browse files Browse the repository at this point in the history
Web app UI fixes
  • Loading branch information
samyakkkk authored Aug 26, 2024
2 parents 8697288 + 63307cf commit 2a6cf59
Show file tree
Hide file tree
Showing 19 changed files with 922 additions and 367 deletions.
74 changes: 74 additions & 0 deletions .github/workflows/discord-bot_discord-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - discord-app

on:
push:
branches:
- discord-bot
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Node.js version
uses: actions/setup-node@v3
with:
node-version: '18.x'

- name: npm install, build, and test
run: |
cd discord
npm install
npm run build --if-present
npm run test --if-present
- name: Zip artifact for deployment
run: |
cd discord
zip -r ../release.zip ./*
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v4
with:
name: node-app
path: release.zip

deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
permissions:
id-token: write #This is required for requesting the JWT

steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: node-app

- name: Unzip artifact for deployment
run: unzip release.zip

- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_F79B5525A24E4DD1B0474AE8E88AC734 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_82993AB6E0F44B589C524B05273985DB }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_BD59F4177F7646289B41D15D6119C77B }}

- name: 'Deploy to Azure Web App'
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: 'discord-app'
slot-name: 'Production'
package: .

3 changes: 3 additions & 0 deletions discord/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
./node_modules/
package-lock.json
196 changes: 196 additions & 0 deletions discord/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
require('dotenv').config();
const { Client, GatewayIntentBits, ChannelType } = require('discord.js');
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMessageReactions,
GatewayIntentBits.GuildMembers
]
});

// Parse the GUILD_AGENT_MAP from the environment variable
const guildAgentMap = JSON.parse(process.env.GUILD_AGENT_MAP);

client.once('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});

client.on('messageCreate', async message => {
console.log('message received');

// Ignore messages from the bot itself
if (message.author.bot) return;

// Check if the bot is tagged in the message
if (message.mentions.has(client.user)) {
const fetch = (await import('node-fetch')).default;

// Function to split message into chunks of 2000 characters or less
function splitMessage(message, maxLength = 2000) {
if (message.length <= maxLength) return [message];
const parts = [];
let currentPart = '';
let inCodeBlock = false;

const lines = message.split('\n');
for (const line of lines) {
if (line.startsWith('```')) {
inCodeBlock = !inCodeBlock;
}

if (currentPart.length + line.length + 1 > maxLength) {
if (inCodeBlock) {
currentPart += '\n```';
parts.push(currentPart);
currentPart = '```';
} else {
parts.push(currentPart);
currentPart = '';
}
}

currentPart += (currentPart.length > 0 ? '\n' : '') + line;

if (!inCodeBlock && currentPart.length >= maxLength) {
parts.push(currentPart);
currentPart = '';
}
}

if (currentPart.length > 0) {
parts.push(currentPart);
}

return parts;
}

// Function to keep typing indicator active
function keepTyping(channel) {
const interval = setInterval(() => {
channel.sendTyping();
}, 5000); // Typing indicator lasts for 10 seconds, so we refresh it every 5 seconds
return interval;
}

// Function to stop typing indicator
function stopTyping(interval) {
clearInterval(interval);
}

// Determine the channel (thread or main channel)
let channel;
if (message.channel.type === ChannelType.PublicThread || message.channel.type === ChannelType.PrivateThread) {
channel = message.channel
} else {
channel = await message.startThread({
name: `Thread for ${message.author.username}`,
autoArchiveDuration: 60,
});

// await channel.send("Hold tight, I'm preparing your answer!\n\nQuick tip ⚡️, I can help you better from your IDE. Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=WelltestedAI.fluttergpt)");
}

// Start typing indicator
const typingInterval = keepTyping(channel);

// Fetch agent details
const guildId = message.guild.id;
const agentName = guildAgentMap[guildId];
if (!agentName) {
channel.send('Sorry, I could not find the agent for this guild.');
stopTyping(typingInterval); // Stop typing indicator
return;
}

let agentDetails;
try {
const response = await fetch("https://api.commanddash.dev/agent/get-latest-agent", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name: agentName }),
});

if (!response.ok) {
throw new Error(`Failed to load the agent: ${await response.json()}`);
}

agentDetails = await response.json();
console.log(agentDetails)
} catch (error) {
console.error('Error fetching agent details:', error);
channel.send('Sorry, I could not fetch the agent details.');
stopTyping(typingInterval); // Stop typing indicator
return;
}

try {
// Fetch all messages in the thread if it's a thread
let history = [];
history.push({ "role": "user", "text": "This conversation is happening on Discord, so please keep response concise and quote snippets only when necessary (unless of course explicity requested) " });
if (channel.type === ChannelType.PublicThread || channel.type === ChannelType.PrivateThread) {
const messages = await channel.messages.fetch({ limit: 100 });
history = messages.map(msg => ({
"role": msg.author.id === client.user.id ? "model" : "user",
"text": msg.content
}));
}

history.push({ "role": "user", "text": message.content });

// Prepare data for agent answer API
const agentData = {
agent_name: agentDetails.name,
agent_version: agentDetails.version,
chat_history: history,
included_references: [],
private: agentDetails.testing,
};

// Get answer from agent
const response = await fetch("https://api.commanddash.dev/v2/ai/agent/answer", {
method: "POST",
body: JSON.stringify(agentData),
headers: {
"Content-Type": "application/json",
},
});
const modelResponse = await response.json();
console.log(modelResponse);

// Split the response into chunks and send each chunk
const responseChunks = splitMessage(modelResponse.response);
for (const chunk of responseChunks) {
await channel.send(chunk);
}
} catch (error) {
console.error('Error getting answer from agent:', error);
channel.send('Sorry, I could not get an answer from the agent.');
} finally {
stopTyping(typingInterval); // Stop typing indicator
}
}
});

// Handle errors to prevent the bot from crashing
client.on('error', error => {
console.error('An error occurred:', error);
});

client.login(process.env.DISCORD_TOKEN);

// API endpoint to return the Discord token
app.get('/api/token', (req, res) => {
res.json({ token: process.env.DISCORD_TOKEN });
});

app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
18 changes: 18 additions & 0 deletions discord/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "discord",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"discord.js": "^14.15.3",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"node-fetch": "^3.3.2"
}
}
25 changes: 25 additions & 0 deletions discord/web.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="index.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True" />
</conditions>
<action type="Rewrite" url="index.js" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
<h1 align="center">CommandDash</h1>
<div align = "center">

[![VScode Downloads](https://img.shields.io/visual-studio-marketplace/d/WelltestedAI.fluttergpt)](https://marketplace.visualstudio.com/items?itemName=WelltestedAI.fluttergpt&ssr=false#overview) [![License: APACHE](https://img.shields.io/badge/License-APACHE%202.0-yellow)](/LICENSE)
[![VScode Downloads](https://img.shields.io/visual-studio-marketplace/d/WelltestedAI.fluttergpt?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=WelltestedAI.fluttergpt&ssr=false#overview) [![License: APACHE](https://img.shields.io/badge/License-APACHE%202.0-yellow?style=for-the-badge)](/LICENSE) <a href="https://app.commanddash.io/agent/dash"><img src="https://img.shields.io/badge/Ask-AI%20Assist-EB9FDA?style=for-the-badge"></a>

</div>

<h3 align="center">Integrate APIs, SDKs or Packages with AI Agents</h3>
Expand Down
2 changes: 1 addition & 1 deletion vscode/media/agent-ui-builder/agent-ui-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AgentUIBuilder {
textHtml = textHtml.replace(`<${input.id}>`, inputElement.outerHTML);
});

activeCommandsAttach.textContent = `${slug}`;
// activeCommandsAttach.textContent = `${slug}`;
this.container.innerHTML = `${textHtml}`;
this.ref.appendChild(this.container);
this.registerCodeInputListener();
Expand Down
19 changes: 13 additions & 6 deletions vscode/media/command-deck/command-deck.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,19 @@ class CommandDeck {
this.ref.innerHTML = textContent.substring(0, atIndex) + textContent.substring(atIndex + 1);
}
if (option?.name.startsWith('@')) {
console.log('agents options', option?.metadata);
activeAgentAttach.style = "color: #497BEF; !important";
activeAgentAttach.textContent = `@${option?.metadata.display_name}`;

agentName = option?.metadata.display_name;
headerLogo.src = option.metadata.avatar_id;
headerText.classList.add("hidden");
headerAgentName.classList.remove("hidden");
headerAgentName.textContent = option?.metadata.display_name;
headerAgentDescription.classList.remove("hidden");
headerAgentDescription.textContent = option?.metadata.description;
activeAgent = true;
commandEnable = false;
activeCommandsAttach.style = "color: var(--vscode-input-placeholderForeground); !important";
activeCommandsAttach.textContent = "/";

currentActiveAgent = option.name;
this.closeMenu();
// Move the cursor to the end of the word
Expand All @@ -164,7 +171,7 @@ class CommandDeck {
setTimeout(() => {
adjustHeight();
commandEnable = true;
activeCommandsAttach.style = "color: rgb(236 72 153); !important";
// activeCommandsAttach.style = "color: rgb(236 72 153); !important";
}, 0);
}
};
Expand Down Expand Up @@ -247,10 +254,10 @@ class CommandDeck {
setTimeout(() => {
if (this.ref.textContent.trim() === "") {
commandEnable = false;
activeCommandsAttach.style = "color: var(--vscode-input-placeholderForeground); !important";
// activeCommandsAttach.style = "color: var(--vscode-input-placeholderForeground); !important";
agentInputsJson.length = 0;
codeInputId = 0;
activeCommandsAttach.textContent = "/";
// activeCommandsAttach.textContent = "/";
}
}, 0);
}
Expand Down
Loading

0 comments on commit 2a6cf59

Please sign in to comment.