Skip to content
This repository has been archived by the owner on Dec 6, 2021. It is now read-only.

Commit

Permalink
0.1.0
Browse files Browse the repository at this point in the history
- Add default locales
- Add logger class without chalk dependency
- Add responder helper
- Add default middleware
- Add built-in command and base classes
- Add emoji JSON to prevent node-emoji dependency
- Add more tests
- Various fixes
  • Loading branch information
pyraxo committed Jan 1, 2017
1 parent 4148aad commit d3b659e
Show file tree
Hide file tree
Showing 28 changed files with 1,987 additions and 335 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ node_modules

test/auth.json

assets
docs
build
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
"main": "build/index.js",
"scripts": {
"docs": "node_modules/.bin/jsdoc --configure .jsdoc.json --verbose",
"prepublish": "gulp",
"pub": "gulp && npm publish",
"install": "gulp",
"test": "gulp && node test/bot.js"
},
"engines": {
"node": ">=6.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/pyraxo/sylphy.git"
"url": "git+https://github.com/pyraxo/sylphy.git",
"type": "git"
},
"keywords": [
"discord",
Expand Down
1 change: 1 addition & 0 deletions res/emoji.json

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions res/i18n/en/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"errors": {
"INSUFFICIENT_ARGS": "insufficient arguments supplied - {{requiredArgs}} expected, but only {{argsCount}} found.",
"NO_END_QUOTE": "you have a missing end quote.",
"ON_COOLDOWN": "please cool down! ({{time}} seconds left)",
"NO_PERMS": "you need the permissions {{perms}} to run this command!",
"NO_PERMS_BOT": "I need the permissions {{perms}} to run this command!",
"NO_PMS": "this command can't be run in PMs."
},
"menus": {
"EXIT": "Enter {{cancel}} to exit the menu.",
"ERRORED": "the menu has closed:",
"EXITED": "you have exited the menu.",
"SELECTION": "Selection Menu",
"ERROR": "Unknown Error",
"INPUT": "Type the number of your choice into chat or '{{cancel}}' to cancel.",
"MORE_RESULTS": "And {{num}} more..."
},
"collector": {
"timeout": "Timeout after {{time}}s",
"max": "Exceeded {{max}} tries",
"maxMatches": "Exceeded {{maxMatches}} matches"
}
}
26 changes: 21 additions & 5 deletions src/core/Client.js → src/Client.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const path = require('path')
const Eris = require('eris').Client

const { Commander, Router, Bridge } = require('./engine')
const { Collection } = require('../util')
const { Commander, Router, Bridge, Interpreter, Logger } = require('./core')
const { Collection } = require('./util')

/**
* Interface between the Discord client and plugins
Expand All @@ -16,16 +16,21 @@ class Client extends Eris {
* Creates a new Client instance
* @arg {Object} options An object containing sylphy's and/or Eris client options
* @arg {String} options.token Discord bot token
* @arg {String} [options.prefix='!'] Default prefix for commands
* @arg {String} [options.admins=[]] Array of admin IDs
* @arg {String} [options.commands] Relative path to commands folder
* @arg {String} [options.modules] Relative path to modules folder
* @arg {String} [options.middleware] Relative path to middleware folder
* @arg {String} [options.locales] Relative path to locales folder
* @arg {Boolean} [options.suppressWarnings=false] Option to suppress console warnings
* @arg {Boolean} [options.noDefaults=false] Option to not use built-in plugins
*/
constructor (options = {}) {
super(options.token, options)
this.prefix = options.prefix || '!'
this.suppressWarnings = options.suppressWarnings
this.noDefaults = options.noDefaults
this.admins = Array.isArray(options.admins) ? options.admins : []

this.plugins = new Collection()

Expand All @@ -34,10 +39,18 @@ class Client extends Eris {
.createPlugin('commands', Commander)
.createPlugin('modules', Router)
.createPlugin('middleware', Bridge)
.createPlugin('i18n', Interpreter)
.createPlugin('logger', Logger)

this.logger = this.plugins.get('logger')

this.register('i18n', path.join(__dirname, '..', 'res/i18n'))
this.register('middleware', path.join(__dirname, 'middleware'))

if (options.commands) this.register('commands', options.commands)
if (options.modules) this.register('modules', options.modules)
if (options.middleware) this.register('middleware', options.middleware)
if (options.locales) this.register('i18n', options.locales)
}
}

Expand Down Expand Up @@ -68,7 +81,7 @@ class Client extends Eris {
if (!plugin) {
throw new Error(`Plugin type ${type} not found`)
}
plugin.register(...args)
if (typeof plugin.register === 'function') plugin.register(...args)
return this
}

Expand All @@ -87,7 +100,7 @@ class Client extends Eris {
if (!plugin) {
throw new Error(`Plugin type ${type} not found`)
}
plugin.unregister(...args)
if (typeof plugin.unregister === 'function') plugin.unregister(...args)
return this
}

Expand All @@ -114,6 +127,9 @@ class Client extends Eris {
if (typeof this.token !== 'string') {
throw new TypeError('No bot token supplied')
}
this.plugins.forEach(plugin => {
if (typeof plugin.run === 'function') plugin.run()
})
this.connect()
return this
}
Expand All @@ -125,7 +141,7 @@ class Client extends Eris {
* @private
*/
throwOrEmit (event, error) {
if (!this.listeners(event, true)) {
if (!this.listeners(event).length) {
throw error
}
this.emit(event, error)
Expand Down
50 changes: 37 additions & 13 deletions src/core/engine/Bridge.js → src/core/Bridge.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const path = require('path')
const fs = require('fs')

const { readdirRecursive, isDir } = require('../../util')
const { readdirRecursive, isDir } = require('../util')

/**
* Middleware manager for commands
Expand All @@ -11,13 +11,14 @@ const { readdirRecursive, isDir } = require('../../util')
class Bridge {
/**
* Creates a new Bridge instance
* @arg {Commander} commander Commander instance
* @arg {Client} client Client instance
*/
constructor (commander) {
constructor (client) {
this.tasks = []
this.collectors = []
this._cached = []
this._commander = commander
this._client = client
this._commander = client.plugins.get('commands')
}

/**
Expand All @@ -33,8 +34,8 @@ class Bridge {
throw new Error(`Folder path ${filepath} does not exist`)
}
this._cached.push(filepath)
const middleware = isDir(filepath) ? readdirRecursive(filepath) : require(filepath)
return this.register(middleware)
const mw = isDir(filepath) ? readdirRecursive(filepath) : require(filepath)
return this.register(mw)
}
case 'object': {
if (Array.isArray(middleware)) {
Expand Down Expand Up @@ -66,12 +67,10 @@ class Bridge {
* Inserts new middleware to the task queue according to ascending priority (lower numbers are earlier in queue)
* @arg {Middleware} middleware Middleware object
*/
push (middleware) {
const priority = middleware.priority || this.tasks.length
if (!middleware.process || !middleware.process.then) {
throw new Error('Middleware must be a promise')
}
this.tasks.splice(priority, 0, middleware)
push (Middleware) {
const middleware = typeof Middleware === 'function' ? new Middleware(this) : Middleware
this.tasks.push(middleware)
this.tasks.sort((a, b) => a.priority - b.priority)
}

/**
Expand Down Expand Up @@ -194,6 +193,31 @@ class Bridge {
return this
}

/** Starts running the bridge */
run () {
this._client.on('messageCreate', msg => {
if (msg.author.id === this._client.user.id || msg.author.bot) return
this.handle({
msg: msg,
client: this._client,
logger: this._client.logger,
admins: this._client.admins,
commands: this._commander,
modules: this._client.plugins.get('modules'),
middleware: this
}).catch(err => {
if (err && this._client.logger) {
this._client.logger.error('Failed to handle message in Bridge -', err)
}
})
})
}

/** Stops running the bridge */
stop () {
this._client.removeAllListeners('messageCreate')
}

/**
* Context container holding a message object along with added properties and objects
* @typedef {Object} Container
Expand All @@ -216,7 +240,7 @@ class Bridge {
}
for (const task of this.tasks) {
try {
const result = await task(container)
const result = await task.process(container)
if (!result) return Promise.reject()
container = result
} catch (err) {
Expand Down
13 changes: 7 additions & 6 deletions src/core/engine/Commander.js → src/core/Commander.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const path = require('path')
const fs = require('fs')
const util = require('util')

const { requireAll, isDir, Collection } = require('../../util')
const { requireAll, isDir, Collection } = require('../util')

/**
* Commander class for command processing
Expand Down Expand Up @@ -36,12 +37,12 @@ class Commander extends Collection {
case 'object': {
if (Array.isArray(commands)) {
for (const command of commands) {
this.commands.attach(command)
this.attach(command)
}
return this
}
for (const group in commands) {
this.commands.attach(commands[group], group)
this.attach(commands[group], group)
}
return this
}
Expand All @@ -67,9 +68,9 @@ class Commander extends Collection {
* @returns {Commander}
*/
attach (Command, group) {
let command = typeof Command === 'function' ? new Command(this._client) : command
if (!command.triggers) {
this._client.throwOrEmit('commander:error', new Error(`Invalid command - ${command}`))
let command = typeof Command === 'function' ? new Command(this._client) : Command
if (!command.triggers || !command.triggers.length) {
this._client.throwOrEmit('commander:error', new Error(`Invalid command - ${util.inspect(command)}`))
return this
}
for (const trigger of command.triggers) {
Expand Down
26 changes: 12 additions & 14 deletions src/core/engine/Interpreter.js → src/core/Interpreter.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
const path = require('path')
const fs = require('fs')

const { requireAll, isDir, Collection } = require('../../util')
const { requireAll, isDir, Collection } = require('../util')

/**
* Locale manager and string parser
*/
/** Locale manager and string parser */
class Interpreter extends Collection {
/**
* Creates a new Localisations instance
Expand Down Expand Up @@ -37,16 +35,16 @@ class Interpreter extends Collection {
if (Array.isArray(strings)) {
for (const pair of strings) {
if (typeof pair[0] !== 'string') continue
this.set(pair[0], pair[1])
this.set(pair[0], Object.assign(this.get(pair[0]) || {}, pair[1]))
}
return this
}
if (!loc) {
for (const lang in strings) {
this.set(lang, strings[lang])
this.set(lang, Object.assign(this.get(lang) || {}, strings[lang]))
}
} else {
this.set(loc, strings)
this.set(loc, Object.assign(this.get(loc) || {}, strings))
}
return this
}
Expand Down Expand Up @@ -93,9 +91,9 @@ class Interpreter extends Collection {
* @arg {String} [locale='en'] The locale to find
* @returns {?String}
*/
get (key = 'common', locale = 'en') {
if (!this.strings.has(locale)) locale = 'en'
return this.locate(key, this.strings.get(locale))
getStrings (key = 'common', locale = 'en') {
if (!this.has(locale)) locale = 'en'
return this.locate(key, this.get(locale))
}

/**
Expand All @@ -116,18 +114,18 @@ class Interpreter extends Collection {
/**
* Parses a string, converting keys to the matching locale string, with interpolation
* @arg {String} string The string to parse
* @arg {String} [group='common'] The string group to use
* @arg {String} [group='default'] The string group to use
* @arg {String} [locale='en'] The locale to use
* @arg {Object} [options] Object containing tags to interpolate into the string
* @returns {String}
*/
parse (string, group = 'common', locale = 'en', options = {}) {
parse (string, group = 'default', locale = 'en', options = {}) {
if (!string) return string
return string.split(' ').map(str => (
str.replace(/\{\{(.+)\}\}/gi, (matched, key) => {
const g = key.startsWith('%') ? 'common.' : group + '.'
const g = key.startsWith('%') ? 'default.' : group + '.'
key = key.startsWith('%') ? key.substr(1) : key
let val = this.get(`${g}${key}`, locale)
let val = this.getStrings(`${g}${key}`, locale)
return typeof val === 'string' ? this.shift(val, options) : matched
})
)).join(' ')
Expand Down
Loading

0 comments on commit d3b659e

Please sign in to comment.