Implement Discord bot starter template

This commit is contained in:
Damjan
2025-01-04 10:52:34 +01:00
parent 61abe10b0d
commit e3a303d67a
9 changed files with 8864 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
DISCORD_BOT_TOKEN=
SOLANA_PRIVATE_KEY=
SOLANA_RPC_URL=
OPENAI_API_KEY=

View File

@@ -0,0 +1,123 @@
module.exports = {
env: {
es2021: true,
node: true,
},
root: true,
settings: {
'import/resolver': {
typescript: {},
},
},
ignorePatterns: [
'.eslintrc.js',
'webpack.config.js',
'dist/*',
'**/*.js',
'node_modules/*',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
project: 'tsconfig.json',
tsconfigRootDir: '.',
sourceType: 'module',
},
extends: [
'airbnb-base',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:prettier/recommended',
'plugin:sonarjs/recommended',
'plugin:security/recommended',
'plugin:promise/recommended',
'prettier',
],
plugins: [
'@typescript-eslint/eslint-plugin',
'sonarjs',
'security',
'promise',
'prettier',
],
rules: {
semi: [2, 'always'],
quotes: [1, 'single', { allowTemplateLiterals: true }],
curly: [2, 'all'],
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-floating-promises': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-misused-promises': [
'error',
{ checksVoidReturn: false },
],
'security/detect-non-literal-regexp': 0,
'security/detect-object-injection': 0,
'promise/always-return': 0,
'promise/no-callback-in-promise': 0,
'sonarjs/cognitive-complexity': [2, 50],
'sonarjs/no-duplicate-string': 0,
'sonarjs/no-useless-catch': 1,
'sonarjs/no-nested-template-literals': 0,
'sonarjs/prefer-single-boolean-return': 1,
'sonarjs/no-small-switch': 'off',
'@typescript-eslint/no-unused-vars': [
1,
{ argsIgnorePattern: '^_|^returns$|^of$|^type$' },
],
'import/extensions': 'off',
'import/no-import-module-exports': 'off',
'import/prefer-default-export': 'off',
'import/no-extraneous-dependencies': 'off',
'import/no-dynamic-require': 'off',
'prettier/prettier': [
'error',
{
useTabs: false,
arrowParens: 'always',
printWidth: 80,
singleQuote: true,
trailingComma: 'all',
endOfLine: 'auto',
bracketSpacing: true,
},
{
usePrettierrc: false,
},
],
'no-restricted-imports': [
'error',
{
patterns: ['**/dist/**'],
},
],
'no-use-before-define': 'off',
'no-console': 'off',
'no-return-await': 'off',
'consistent-return': 'off',
'default-case': 'off',
'no-fallthrough': 'off',
'no-plusplus': 'off',
'no-await-in-loop': 'off',
'no-restricted-syntax': 'off',
'no-continue': 'off',
'no-nested-ternary': 'off',
'no-void': 'off',
'no-param-reassign': 'off',
'class-methods-use-this': 'off',
'no-return-assign': 'off',
'no-case-declarations': 'off',
'global-require': 'off',
'security/detect-non-literal-require': 'off',
'global-require': 'off',
},
};

View File

@@ -0,0 +1,8 @@
.env
*.log
.DS_Store
logs/
node_modules/
build/
dist/

View File

@@ -0,0 +1,10 @@
{
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "auto",
"bracketSpacing": true
}

View File

@@ -0,0 +1,41 @@
# Discord Bot Starter
This is a starter template for creating a Discord bot using the Solana Agent Kit by Send AI.
## Setup
### Prerequisites
- Node.js (v20 or higher)
- pnpm (v9 or higher)
- A Discord account
- A Solana account keypair
### Step 1: Create a Discord Bot
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications).
2. Click on "New Application" and give your application a name.
3. Navigate to the "Bot" tab on the left sidebar and click "Add Bot".
4. Under the "Token" section, click "Copy" to copy your bot token.
### Step 2: Fill Out Environment Variables
Create a `.env` file in the root directory of the project and fill it out with the following variables:
- `DISCORD_BOT_TOKEN`: Paste the bot token you copied from the Discord Developer Portal.
- `SOLANA_PRIVATE_KEY`: Enter your Solana private key. This is required for interacting with the Solana blockchain.
- `SOLANA_RPC_URL`: Provide the RPC URL for connecting to the Solana network. You can use a public RPC URL or your own.
- `OPENAI_API_KEY`: Input your OpenAI API key if you plan to use OpenAI services within your bot. You can obtain this key from the OpenAI platform.
### Step 3: Install Dependencies and Start the Bot
1. Open a terminal and navigate to the root directory of the project.
2. Run the following command to install the project dependencies:
```sh
pnpm install
```
3. After the installation is complete, start the bot by running:
```sh
pnpm start
```
4. Once the bot is running, open Discord and send a direct message (DM) to your bot to ensure it is working correctly.

View File

@@ -0,0 +1,39 @@
{
"name": "discord-bot-starter",
"version": "1.0.0",
"description": "Discord bot starter template using the Solana Agent Kit by Send AI",
"main": "index.ts",
"scripts": {
"start": "nodemon ./src/index.ts",
"lint": "eslint -c .eslintrc.js --ext .ts ./src",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\""
},
"author": "dimitrov-d",
"dependencies": {
"discord.js": "^14.17.2",
"dotenv": "^16.4.7",
"solana-agent-kit": "^1.3.4"
},
"devDependencies": {
"@types/node": "^22.10.5",
"@typescript-eslint/parser": "8.19.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-security": "^3.0.1",
"eslint-plugin-sonarjs": "^3.0.1",
"nodemon": "^3.1.9",
"prettier": "^3.4.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
},
"nodemonConfig": {
"ext": "*.ts",
"exec": "tsx",
"delay": 1000
}
}

8533
examples/discord-bot-starter/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
import 'dotenv/config';
import { Client, GatewayIntentBits, Events, ChannelType, Partials } from 'discord.js';
import { HumanMessage } from '@langchain/core/messages';
import { MemorySaver } from '@langchain/langgraph';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { ChatOpenAI } from '@langchain/openai';
import { SolanaAgentKit, createSolanaTools } from 'solana-agent-kit';
const client = new Client({
intents: [GatewayIntentBits.MessageContent, GatewayIntentBits.DirectMessages],
partials: [Partials.Message, Partials.Channel],
});
async function initializeAgent() {
try {
const llm = new ChatOpenAI({
modelName: 'gpt-4o-mini',
temperature: 0.3,
});
const solanaAgent = new SolanaAgentKit(process.env.SOLANA_PRIVATE_KEY!, process.env.SOLANA_RPC_URL!, {
OPENAI_API_KEY: process.env.OPENAI_API_KEY!,
// other config options here
});
const tools = createSolanaTools(solanaAgent);
const memory = new MemorySaver();
const config = { configurable: { thread_id: 'Solana Agent Kit!' } };
const agent = createReactAgent({
llm,
tools,
checkpointSaver: memory,
messageModifier: `
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you
can't do with your currently available tools, you must say so, and encourage them to implement it
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
`,
});
return { agent, config };
} catch (error) {
console.error('Failed to initialize agent:', error);
throw error;
}
}
client.on(Events.ClientReady, async () => {
// gets data about the bot
await client.application?.fetch();
console.info(`${client.user?.username || 'Bot'} is running. Send it a message in Discord DM to get started.`);
});
client.on(Events.MessageCreate, async (message) => {
try {
if (message.channel.type !== ChannelType.DM || message.author.bot) {
return;
}
console.info(`Received message: ${message.content}`);
await message.channel.sendTyping();
const { agent, config } = await initializeAgent();
const stream = await agent.stream({ messages: [new HumanMessage(message.content)] }, config);
const replyIfNotEmpty = async (content: string) => content.trim() !== '' && message.reply(content);
for await (const chunk of stream) {
if ('agent' in chunk) {
await replyIfNotEmpty(chunk.agent.messages[0].content);
}
}
} catch (error) {
console.error('Error handling message:', error);
}
});
client.login(process.env.DISCORD_BOT_TOKEN);

View File

@@ -0,0 +1,21 @@
{
"compileOnSave": true,
"compilerOptions": {
"baseUrl": ".",
"rootDir": "./src",
"outDir": "./build",
"sourceMap": true,
"declaration": false,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2020",
"typeRoots": ["node_modules/@types"],
"lib": ["es2018", "dom", "esnext.asynciterable"],
"plugins": [{ "transform": "typescript-transform-paths" }],
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
}
}