-
Notifications
You must be signed in to change notification settings - Fork 2
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
0 parents
commit 07c326a
Showing
8 changed files
with
395 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,13 @@ | ||
data-store.json | ||
config.json | ||
/node_modules/* | ||
/bin/* | ||
guild-storage.json | ||
bot-storage.json | ||
npm-debug* | ||
bottest.js | ||
/pkg/* | ||
/storage/* | ||
/typings/* | ||
/.vscode/* | ||
yarn* |
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,29 @@ | ||
# dm-manager | ||
`dm-manager` is a plugin for Discord bots that are written with YAMDBF/Discord.js that provides for bot authors an interface with which they can respond to users who DM the bot. | ||
|
||
After I started logging bot command usage I realized that users often begin DMing the bot when they don't understand how the bot works and in the case of my bots, despite the presence of a help command, they still DM random things. This offers an opportunity to respond to them with an easy interface. | ||
|
||
> Note: Despite being considered a [YAMDBF](https://github.com/zajrik/yamdbf) Addon, this is completely compatible with any bot written using Discord.js. | ||
# Getting started | ||
|
||
Install the package via `npm`: | ||
``` | ||
npm install --save yamdbf-addon-dm-manager | ||
``` | ||
|
||
Before anything else, you need to create an empty guild and invite your bot to it. Then you must make sure the bot has `Manage Channels` and `Manage Messages` permissions in this guild. After that you just need to import the module and add a bit of code to your ready event. You can store the DMManager anywhere when constructing; I find it easiest to stick onto the Bot object itself. | ||
|
||
```js | ||
const { DMManager } = require('yamdbf-addon-dm-manager'); | ||
// ... | ||
bot.once('ready', () => { | ||
bot.dmManager = new DMManager(bot, 'ID of the guild you set up'); | ||
}); | ||
``` | ||
|
||
That's all there is to it. Whenever a DM is sent to the bot from a user, a channel will be made for the user in the guild you passed into the DMManager on construction. Any further DMs sent by the user will be sent to that channel. Any message you send in that channel will be forwarded it to user via DM from the bot. | ||
|
||
To close a DM channel, simply delete the channel from the guild from within your Discord client. If the user DMs the bot again, a new managed channel will be created for them. | ||
|
||
If you find any problems or have any suggestions, don't hesitate to open up an issue. |
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,43 @@ | ||
var gulp = require('gulp'); | ||
var ts = require('gulp-typescript'); | ||
var del = require('del'); | ||
|
||
gulp.task('default', () => | ||
{ | ||
del.sync(['./bin/**/*.*']); | ||
gulp.src('./src/**/*.ts') | ||
.pipe(ts({ | ||
noImplicitAny: true, | ||
outDir: 'bin', | ||
target: 'ES6', | ||
module: 'commonjs', | ||
moduleResolution: 'node' | ||
})) | ||
.pipe(gulp.dest('bin/')); | ||
gulp.src('./src/config.json') | ||
.pipe(gulp.dest('bin/')); | ||
}); | ||
|
||
gulp.task('package', (done) => | ||
{ | ||
gulp.src('src/**/*ts') | ||
.pipe(ts({ | ||
noImplicitAny: true, | ||
outDir: 'bin', | ||
target: 'ES6', | ||
module: 'commonjs', | ||
moduleResolution: 'node' | ||
})) | ||
.pipe(gulp.dest('pkg/yamdbf-addon-dm-manager/bin')); | ||
gulp.src('src/**/*.json') | ||
.pipe(gulp.dest('pkg/yamdbf-addon-dm-manager/bin')); | ||
gulp.src(['package.json', '*.md']) | ||
.pipe(gulp.dest('pkg/yamdbf-addon-dm-manager')); | ||
done(); | ||
}); | ||
|
||
gulp.task('clean-package', (done) => | ||
{ | ||
del.sync(['pkg/yamdbf-addon-dm-manager/bin/**', '!pkg/yamdbf-addon-dm-manager/bin']); | ||
done(); | ||
}); |
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,38 @@ | ||
{ | ||
"name": "yamdbf-addon-dm-manager", | ||
"version": "0.1.0", | ||
"description": "YAMDBF addon for viewing and replying to DMs sent to your discord bot", | ||
"main": "bin/index.js", | ||
"scripts": { | ||
"lint": "tslint './src/**/*.ts'", | ||
"package": "gulp clean-package && gulp package" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/zajrik/yamdbf-addon-dm-manager.git" | ||
}, | ||
"keywords": [ | ||
"discord", | ||
"bot", | ||
"yamdbf", | ||
"node" | ||
], | ||
"author": "Zackary Campbell <[email protected]>", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/zajrik/yamdbf-addon-dm-manager/issues" | ||
}, | ||
"homepage": "https://github.com/zajrik/yamdbf-addon-dm-manager#readme", | ||
"dependencies": { | ||
"discord.js": "^11.0.0", | ||
"yamdbf": "^2.6.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^6.0.46", | ||
"del": "^2.2.2", | ||
"gulp": "^3.9.1", | ||
"gulp-typescript": "^3.1.0", | ||
"tslint": "^3.15.1", | ||
"typescript": "^2.1.4" | ||
} | ||
} |
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,159 @@ | ||
import { LocalStorage } from 'yamdbf'; | ||
import { Client, Message, Guild, User, TextChannel, DMChannel, Collection, RichEmbed } from 'discord.js'; | ||
|
||
export default class DMManager | ||
{ | ||
private client: Client; | ||
private _guild: Guild; | ||
private storage: LocalStorage; | ||
private channels: Collection<string, TextChannel>; | ||
|
||
public constructor(bot: Client, guild: string) | ||
{ | ||
this.client = bot; | ||
if (!this.client.guilds.has(guild)) | ||
throw new Error(`DMManager: Failed to find guild with ID '${guild}'`); | ||
|
||
this.storage = new LocalStorage('storage/DMManager'); | ||
|
||
this.guild = guild; | ||
if (!this._guild.member(this.client.user).hasPermissions(['MANAGE_CHANNELS', 'MANAGE_MESSAGES'])) | ||
throw new Error('DMManager: Bot must have MANAGE_CHANNELS, MANAGE_MESSAGES permissions in the supplied guild'); | ||
|
||
this.channels = new Collection<string, TextChannel>( | ||
(this.storage.getItem('openChannels') || []).map((c: [string, string]) => | ||
[c[0], this._guild.channels.get(c[1])]) || []); | ||
|
||
this.client.on('message', (message: Message) => this.handleMessage(message)); | ||
this.client.on('channelDelete', (channel: TextChannel) => | ||
{ | ||
if (this.channels.find((c: TextChannel) => c.id === channel.id)) | ||
{ | ||
this.channels.delete(this.channels.findKey((c: TextChannel) => c.id === channel.id)); | ||
this.storeOpenChannels(); | ||
} | ||
}); | ||
} | ||
|
||
private get guild(): string { return this._guild.id; } | ||
|
||
/** | ||
* If guild does not match the guild in storage, assume | ||
* the manager has been assigned to a new guild and remove | ||
* open channels from storage as they no longer need to be | ||
* tracked | ||
*/ | ||
private set guild(value) | ||
{ | ||
this._guild = this.client.guilds.get(value); | ||
if (this.storage.exists('guild') | ||
&& this.storage.getItem('guild') !== value) | ||
this.clearOpenChannels(); | ||
} | ||
|
||
/** | ||
* Update open managed channels in storage | ||
*/ | ||
private storeOpenChannels(): void | ||
{ | ||
this.storage.setItem('openChannels', Array.from(this.channels.entries()) | ||
.map((c: [string, TextChannel]) => [c[0], c[1].id])); | ||
} | ||
|
||
/** | ||
* Remove any open channels from storage | ||
*/ | ||
private clearOpenChannels(): void | ||
{ | ||
this.storage.setItem('openChannels', []); | ||
this.channels = new Collection<string, TextChannel>(); | ||
} | ||
|
||
/** | ||
* Create a new managed channel for the user in the dm manager | ||
* guild and add it to the channels cache and stored openChannels | ||
*/ | ||
private async createNewChannel(user: User): Promise<TextChannel> | ||
{ | ||
const newChannel: TextChannel = <TextChannel> await this._guild | ||
.createChannel(`${user.username}-${user.discriminator}`, 'text'); | ||
this.channels.set(user.id, newChannel); | ||
this.storeOpenChannels(); | ||
|
||
await newChannel.sendEmbed(this.buildUserInfo(user)); | ||
return newChannel; | ||
} | ||
|
||
/** | ||
* Create an embed for user info used at the start | ||
* of a new managed channel | ||
*/ | ||
private buildUserInfo(user: User): RichEmbed | ||
{ | ||
return new RichEmbed() | ||
.setColor(8450847) | ||
.setAuthor(`${user.username}#${user.discriminator} (${user.id})`, user.avatarURL) | ||
.setFooter('DM channel started') | ||
.setTimestamp(); | ||
} | ||
|
||
/** | ||
* Handle incoming messages. If it's a DM, find the channel | ||
* belonging to the user. If it doesn't exist, create one | ||
*/ | ||
private async handleMessage(message: Message): Promise<void> | ||
{ | ||
if (message.embeds[0] && message.channel.type !== 'dm') return; | ||
if (message.channel.type !== 'dm' && message.guild.id !== this.guild) return; | ||
if (message.author.id !== this.client.user.id && !this.channels.has(message.author.id)) | ||
await this.createNewChannel(message.author); | ||
|
||
if (message.channel.type === 'dm') | ||
{ | ||
const channelID: string = message.author.id === this.client.user.id ? | ||
(<DMChannel> message.channel).recipient.id : message.author.id; | ||
const channel: TextChannel = this.channels.get(channelID); | ||
if (message.embeds[0]) message.content += '\n\n**[RichEmbed]**'; | ||
await this.send(channel, message.author, message.content); | ||
} | ||
else | ||
{ | ||
message.delete(); | ||
const user: User = await this.fetchUser(<TextChannel> message.channel); | ||
try | ||
{ | ||
await user.send(message.content); | ||
} | ||
catch (err) | ||
{ | ||
message.channel.sendEmbed(new RichEmbed() | ||
.setColor('#FF0000') | ||
.setTitle('There was an error while sending the message') | ||
.setDescription(err)); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Fetch the user object the managed channel represents contact with | ||
*/ | ||
private async fetchUser(channel: TextChannel): Promise<User> | ||
{ | ||
const id: string = this.channels.findKey('id', channel.id); | ||
return await this.client.fetchUser(id); | ||
} | ||
|
||
/** | ||
* Send a text message to a managed channel as an embed, spoofing | ||
* the provided user to simulate messages from that user | ||
*/ | ||
private async send(channel: TextChannel, user: User, message: string): Promise<Message> | ||
{ | ||
return await channel.sendEmbed( | ||
new RichEmbed() | ||
.setColor(8450847) | ||
.setAuthor(`${user.username}#${user.discriminator}`, user.avatarURL) | ||
.setDescription(message) | ||
.setTimestamp()); | ||
} | ||
} |
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 @@ | ||
import _DMManager from './DMManager'; | ||
|
||
export const DMManager = _DMManager; // tslint:disable-line | ||
export default DMManager; |
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,20 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es6", | ||
"module": "commonjs", | ||
"lib": [ | ||
"es7" | ||
] | ||
}, | ||
"exclude": [ | ||
"node_modules", | ||
"bower_components", | ||
"jspm_packages", | ||
"tmp", | ||
"temp", | ||
"bin", | ||
"docs", | ||
"pkg", | ||
"examples" | ||
] | ||
} |
Oops, something went wrong.