-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
40 changed files
with
22,540 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
dist/ | ||
node_modules/ | ||
.env | ||
.env | ||
*.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
dist/ | ||
node_modules/ | ||
coverage/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
src | ||
tsconfig.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Discord Plugin for Virtuals Game | ||
|
||
This plugin allows you to integrate Discord functionalities into your Virtuals Game. With this plugin, you can send message, pin message, add reaction and delete message in Discord. | ||
|
||
## Installation | ||
|
||
To install the plugin, use npm or yarn: | ||
|
||
```bash | ||
npm install @virtuals-protocol/game-discord-plugin | ||
``` | ||
|
||
or | ||
|
||
```bash | ||
yarn add @virtuals-protocol/game-discord-plugin | ||
``` | ||
|
||
## Usage | ||
|
||
### Importing the Plugin | ||
|
||
First, import the `DiscordPlugin` class from the plugin: | ||
|
||
```typescript | ||
import DiscordPlugin from "@virtuals-protocol/game-discord-plugin"; | ||
``` | ||
|
||
### Creating a Worker | ||
|
||
Create a worker with the necessary DiscordPlugin credentials: | ||
|
||
```typescript | ||
const discordPlugin = new DiscordPlugin({ | ||
credentials: { | ||
botToken: "<BOT TOKEN>" | ||
}, | ||
}); | ||
``` | ||
|
||
### Creating an Agent | ||
|
||
Create an agent and add the worker to it: | ||
|
||
```typescript | ||
import { GameAgent } from "@virtuals-protocol/game"; | ||
|
||
const agent = new GameAgent("<API_KEY>", { | ||
name: "Discord Bot", | ||
goal: "increase engagement and grow follower count", | ||
description: "A bot that can reply message, add reaction, pin message and delete message in Discord.", | ||
workers: [ | ||
discordPlugin.getWorker({ | ||
// Define the functions that the worker can perform, by default it will use the all functions defined in the plugin | ||
functions: [ | ||
discordPlugin.sendMessageFunction, | ||
discordPlugin.addReactionFunction, | ||
discordPlugin.pinMessageFunction, | ||
discordPlugin.deleteMessageFunction, | ||
], | ||
}), | ||
], | ||
}); | ||
``` | ||
|
||
### Running the Agent | ||
|
||
Initialize and run the agent: | ||
|
||
```typescript | ||
(async () => { | ||
await agent.init(); | ||
|
||
while (true) { | ||
await agent.step({ | ||
verbose: true, | ||
}); | ||
} | ||
})(); | ||
``` | ||
|
||
## Available Functions | ||
|
||
The `DiscordPlugin` provides several functions that can be used by the agent: | ||
|
||
- `sendMessageFunction`: Send a message in discord | ||
- `addReactionFunction`: Add a reaction in discord message. | ||
- `pinMessageFunction`: Pin a message in discord message. | ||
- `deleteMessageFunction`: Delete a message in discord. | ||
|
||
## Event Handlers | ||
The plugin also supports custom handlers for the following Discord events: | ||
### Handling Incoming Messages | ||
To handle incoming messages, use the `onMessage` method to listen on: | ||
```typescript | ||
discordPlugin.onMessage((msg) => { | ||
console.log("Received message:", msg); | ||
}); | ||
``` | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import DiscordPlugin from '../src/discordPlugin'; | ||
import { Client } from 'discord.js'; | ||
import { ExecutableGameFunctionStatus } from "@virtuals-protocol/game"; | ||
|
||
// Mock discord.js | ||
jest.mock('discord.js', () => ({ | ||
Client: jest.fn().mockImplementation(() => ({ | ||
login: jest.fn().mockImplementation(() => ({ | ||
then: jest.fn((successCallback) => { | ||
successCallback(); | ||
return { | ||
catch: jest.fn() | ||
}; | ||
}), | ||
catch: jest.fn() | ||
})), | ||
on: jest.fn().mockImplementation(() => {}), | ||
once: jest.fn().mockImplementation(() => {}), | ||
// Mock the channels property | ||
channels: { | ||
fetch: jest.fn().mockResolvedValue({ | ||
isTextBased: jest.fn().mockReturnValue(true), | ||
send: jest.fn().mockResolvedValue({}), | ||
// Mock the messages property | ||
messages: { | ||
fetch: jest.fn().mockResolvedValue({ | ||
react: jest.fn(), | ||
pin: jest.fn(), | ||
delete: jest.fn(), | ||
send: jest.fn() | ||
}) | ||
} | ||
}) | ||
} | ||
})), | ||
GatewayIntentBits: { | ||
Guilds: 'Guilds', | ||
GuildMessages: 'GuildMessages', | ||
MessageContent: 'MessageContent', | ||
GuildMessageReactions: 'GuildMessageReactions', | ||
DirectMessages: 'DirectMessages', | ||
DirectMessageReactions: 'DirectMessageReactions', | ||
DirectMessageTyping: 'DirectMessageTyping' | ||
} | ||
})); | ||
|
||
describe('DiscordPlugin', () => { | ||
const discordPlugin = new DiscordPlugin({ | ||
credentials: { botToken: 'mock-token' } | ||
}); | ||
|
||
describe('Constructor', () => { | ||
it('should initialize with default values', () => { | ||
expect(discordPlugin['id']).toBe('discord_worker'); | ||
expect(discordPlugin['name']).toBe('Discord Worker'); | ||
expect(discordPlugin['description']).toBeDefined(); | ||
}); | ||
|
||
it('should create a Discord client with correct intents', () => { | ||
expect(Client).toHaveBeenCalledWith(expect.objectContaining({ | ||
intents: expect.any(Array) | ||
})); | ||
}); | ||
}); | ||
|
||
describe('getWorker', () => { | ||
it('should return a GameWorker with default functions', () => { | ||
const worker = discordPlugin.getWorker(); | ||
|
||
expect(worker).toBeDefined(); | ||
expect(worker.functions).toHaveLength(4); | ||
expect(worker.functions.map(f => f.name)).toEqual([ | ||
'send_message', | ||
'add_reaction', | ||
'pin_message', | ||
'delete_message' | ||
]); | ||
}); | ||
}); | ||
|
||
describe('Game Functions', () => { | ||
|
||
describe('sendMessageFunction', () => { | ||
it('should send message successfully when valid arguments passed in', async () => { | ||
const logger = jest.fn(); | ||
const result = await discordPlugin.sendMessageFunction.executable( | ||
{ channel_id: '123', content: 'Send this message' }, | ||
logger | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Done); | ||
expect(result.feedback).toContain('Message sent successfully.'); | ||
}); | ||
|
||
it('should fail if channel_id or content is missing', async () => { | ||
const result = await discordPlugin.sendMessageFunction.executable( | ||
{ channel_id: '', content: '' }, | ||
jest.fn() | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Failed); | ||
expect(result.feedback).toContain('Both channel_id and content are required'); | ||
}); | ||
}); | ||
|
||
describe('addReactionFunction', () => { | ||
it('should add reaction successfully when required argument is passed', async () => { | ||
const result = await discordPlugin.addReactionFunction.executable( | ||
{ channel_id: '123', message_id: '321', emoji: '😊' }, | ||
jest.fn() | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Done); | ||
expect(result.feedback).toContain('Reaction added successfully.'); | ||
}); | ||
|
||
it('should fail if any required argument is missing', async () => { | ||
const result = await discordPlugin.addReactionFunction.executable( | ||
{ channel_id: '', message_id: '', emoji: '' }, | ||
jest.fn() | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Failed); | ||
expect(result.feedback).toContain('channel_id, message_id, and emoji are required'); | ||
}); | ||
}); | ||
|
||
describe('pinMessageFunction', () => { | ||
it('should success pin message if required arguments is passed', async () => { | ||
const result = await discordPlugin.pinMessageFunction.executable( | ||
{ channel_id: '123', message_id: '321' }, | ||
jest.fn() | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Done); | ||
expect(result.feedback).toContain('Message pinned successfully.'); | ||
}); | ||
|
||
it('should fail if channel_id or message_id is missing', async () => { | ||
const result = await discordPlugin.pinMessageFunction.executable( | ||
{ channel_id: '', message_id: '' }, | ||
jest.fn() | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Failed); | ||
expect(result.feedback).toContain('Both channel_id and message_id are required'); | ||
}); | ||
}); | ||
|
||
describe('deleteMessageFunction', () => { | ||
it('should delete message successfully', async () => { | ||
const result = await discordPlugin.deleteMessageFunction.executable( | ||
{ channel_id: '123', message_id: '321' }, | ||
jest.fn() | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Done); | ||
expect(result.feedback).toContain('Message deleted successfully.'); | ||
}); | ||
|
||
it('should fail if channel_id or message_id is missing', async () => { | ||
const result = await discordPlugin.deleteMessageFunction.executable( | ||
{ channel_id: '', message_id: '' }, | ||
jest.fn() | ||
); | ||
|
||
expect(result.status).toBe(ExecutableGameFunctionStatus.Failed); | ||
expect(result.feedback).toContain('Both channel_id and message_id are required'); | ||
}); | ||
}); | ||
|
||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
}; |
Oops, something went wrong.