Pushing changes

This commit is contained in:
2017-03-23 23:52:08 -05:00
parent 6075860b82
commit ac667ec74f
1465 changed files with 345149 additions and 3 deletions

View File

@ -0,0 +1,327 @@
"use strict";
const IBase = require("./IBase");
const IUser = require("./IUser");
const Utils = require("../core/Utils");
const AuthenticatedUser = require("../models/AuthenticatedUser");
const Constants = require("../Constants");
const StatusTypes = Constants.StatusTypes;
const rest = require("../networking/rest");
/**
* @interface
* @model AuthenticatedUser
* @extends IBase
*/
class IAuthenticatedUser extends IBase {
constructor(discordie) {
super();
Utils.definePrivate(this, {_discordie: discordie});
Object.freeze(this);
}
/**
* Gets date and time the account was registered (created) at.
* @returns {Date}
* @readonly
*/
get registeredAt() {
return new Date(Utils.timestampFromSnowflake(this.id));
}
/**
* Gets current JPG or GIF avatar URL.
* @returns {String|null}
* @readonly
*/
get avatarURL() {
return Object.getOwnPropertyDescriptor(IUser.prototype, "avatarURL")
.get.apply(this, arguments);
}
/**
* Gets current JPG avatar URL.
* @returns {String|null}
* @readonly
*/
get staticAvatarURL() {
return Object.getOwnPropertyDescriptor(IUser.prototype, "staticAvatarURL")
.get.apply(this, arguments);
}
/**
* Gets a value indicating whether the account is claimed.
* @returns {boolean}
* @readonly
*/
get isClaimedAccount() {
return this.email != null;
}
/**
* Checks whether the user is mentioned in a `message`.
* @param {IMessage} message
* @param {boolean} ignoreImplicitMentions
* @returns {boolean}
*/
isMentioned(message, ignoreImplicitMentions) {
return IUser.prototype.isMentioned.apply(this, arguments);
}
/**
* Makes a request to get the bot's OAuth2 application info.
*
* Works only with bot accounts.
*
* Example response object:
* ```js
* {
* "description": "Test",
* "icon": null,
* "id": "179527948411052118",
* "name": "app name or something",
* "rpc_origins": [],
* "flags": 0,
* "owner": {
* "username": "bot owner",
* "discriminator": "4937",
* "id": "169454786183781631",
* "avatar": null
* }
* }
* ```
* @returns {Promise<Object, Error>}
*/
getApplication() {
return rest(this._discordie).oauth2.getApplication(Constants.ME);
}
/**
* Makes a request to edit user profile,
* substituting `undefined` or `null` properties with current values.
*
* Passing `null` in `avatar` will remove current avatar. Use `undefined`
* instead of `null` in this case.
* @param {String} currentPassword - Use null as password on bot accounts
* @param {String} [username]
* @param {String|Buffer|null} [avatar] - Buffer or base64 data URL
* @param {String} [email]
* @param {String} [newPassword]
* @returns {Promise<IUser, Error>}
* @example
* // avatar from file
* client.User.edit(currentPassword, null, fs.readFileSync("test.png"));
* client.User.edit(currentPassword, null, fs.readFileSync("test.jpg"));
* // avatar unchanged
* client.User.edit(currentPassword, "test", undefined, "new@example.com");
* client.User.edit(currentPassword, "test", client.User.avatar);
* // no avatar / default avatar
* client.User.edit(currentPassword, "test", null);
*/
edit(currentPassword, username, avatar, email, newPassword) {
const user = this._discordie._user;
username = username || user.username;
email = email || user.email;
newPassword = newPassword || null;
if (avatar instanceof Buffer) {
avatar = Utils.imageToDataURL(avatar);
} else if (avatar === undefined) {
avatar = user.avatar;
}
return new Promise((rs, rj) => {
rest(this._discordie)
.users.me(currentPassword, username, avatar, email, newPassword)
.then(() => rs(this._discordie.User))
.catch(rj);
});
}
/**
* Makes a request to edit avatar.
*
* Setting avatar to `null` will remove current avatar.
* @param {String|Buffer|null} avatar - Buffer or base64 data URL
* @param {String} [currentPassword] - Not applicable for bot accounts
* @returns {Promise<IUser, Error>}
* @example
* // avatar from file
* client.User.setAvatar(fs.readFileSync("test.png"));
* // remove avatar
* client.User.setAvatar(null);
*/
setAvatar(avatar, currentPassword) {
return this.edit(currentPassword, null, avatar);
}
/**
* Makes a request to edit username.
* @param {String} username
* @param {String} [currentPassword] - Not applicable for bot accounts
* @returns {Promise<IUser, Error>}
*/
setUsername(username, currentPassword) {
return this.edit(currentPassword, username);
}
/**
* Sets current user status via websocket.
*
* With multiple sessions status from last connected will override statuses
* from previous sessions.
*
* > Note: By default Discord client does not display game/status updates for
* > the user it's logged in into. It will be visible for other users.
* @param {Object|String} status
* Object `{status: String, afk: boolean}` or string.
* Field `afk` changes how Discord handles push notifications.
* @param {Object|String|null} [game] - Object `{name: String}` or string
* @example
* var game = {name: "with code"}; // sets status as "Playing with code"
* var streamingGame = {type: 1, name: "something", url: ""}; // "Streaming"
* // note: streaming status requires a valid twitch url:
* // ex. "http://twitch.tv/channel"
* client.User.setStatus("idle", game); // idle, playing
* client.User.setStatus(null, game); // no status change, playing
* client.User.setStatus(null, "with code"); // no status change, playing
* client.User.setStatus(null, streamingGame); // no status change, streaming
* client.User.setStatus("online", game); // online, playing
* client.User.setStatus("idle", null); // idle, not playing
* client.User.setStatus("dnd"); // "do not disturb"
* client.User.setStatus("invisible");
* client.User.setStatus({status: "idle", afk: true});
*/
setStatus(status, game) {
if (arguments.length == 0) return;
var afk = this.afk;
if (!status) status = this.status;
if (typeof status === "object") {
afk = status.afk != null ? !!status.afk : this.afk;
status = status.status != null ? status.status : this.status;
}
if (game === undefined) game = this.game;
if (typeof game === "string") game = {name: game};
status = Object.keys(StatusTypes).map(v => StatusTypes[v])
.find(v => v === status.toLowerCase());
status = status || StatusTypes.ONLINE;
if (this._discordie._user) {
this._discordie._user = this._discordie._user.merge({status, game, afk});
}
if (!this._discordie.gatewaySocket) return;
this._discordie.gatewaySocket.statusUpdate(
status,
status === StatusTypes.IDLE ? Date.now() : null,
game,
afk
);
}
/**
* Sets playing game for current user via websocket.
*
* With multiple sessions status from last connected will override statuses
* from previous sessions.
*
* > Note: By default Discord client does not display game/status updates for
* > the user it's logged in into. It will be visible for other users.
* @param {Object|String|null} game - Object `{name: String}` or string
* @example
* var game = {name: "with code"}; // sets game as "Playing with code"
* var streamingGame = {type: 1, name: "something", url: ""}; // "Streaming"
* // note: streaming status requires a valid twitch url:
* // ex. "http://twitch.tv/channel"
* client.User.setGame(game); // playing
* client.User.setGame("with code"); // playing
* client.User.setGame(streamingGame); // streaming
* client.User.setGame(null); // not playing
*/
setGame(game) {
if (arguments.length == 0) return;
this.setStatus(null, game);
}
/**
* Name of the game current user is playing.
* @returns {String|null}
* @readonly
*/
get gameName() {
return this.game ? this.game.name : null;
}
/**
* Attempts to get a guild member interface, returns null if this user is not
* a member of the `guild` or `guild` is not in cache.
* @param {IGuild|String} guild
* @returns {IGuildMember|null}
*/
memberOf(guild) {
return this._discordie.Users.getMember(guild.valueOf(), this.id) || null;
}
/**
* See `IUser.permissionsFor`.
* @see IUser.permissionsFor
* @param {IChannel|IGuild} context
* @returns {IPermissions}
*/
permissionsFor(context) {
return IUser.prototype.permissionsFor.apply(this, arguments);
}
/**
* See `IUser.can`.
* @see IUser.can
* @param {Number} permission - One or multiple permission bits
* @param {IChannel|IGuild} context
* @returns {boolean}
*/
can(permission, context) {
return IUser.prototype.can.apply(this, arguments);
}
/**
* See `IUser.getVoiceChannel`.
* @see IUser.getVoiceChannel
* @param {IGuild|String} guild - Guild or an id string
* @returns {IVoiceChannel|null}
*/
getVoiceChannel(guild) {
return IUser.prototype.getVoiceChannel.apply(this, arguments);
}
/**
* Creates a mention from this user's id.
* @returns {String}
* @readonly
* @example
* channel.sendMessage(user.mention + ", example mention");
*/
get mention() {
return `<@${this.id}>`;
}
/**
* Creates a nickname mention from this user's id.
* @returns {String}
* @readonly
*/
get nickMention() {
return `<@!${this.id}>`;
}
}
IAuthenticatedUser._inherit(AuthenticatedUser, function(key) {
return this._discordie._user[key];
});
module.exports = IAuthenticatedUser;

157
node_modules/discordie/lib/interfaces/IBase.js generated vendored Normal file
View File

@ -0,0 +1,157 @@
"use strict";
const Utils = require("../core/Utils");
class IBase {
static _inherit(proto, get) {
var thisProto = this.prototype;
thisProto._valueOverrides = thisProto._valueOverrides || {};
thisProto._gettersByProperty = thisProto._gettersByProperty || {};
thisProto._suppressErrors = false;
Object.defineProperties(thisProto, {
_valueOverrides: {enumerable: false},
_gettersByProperty: {enumerable: false},
_suppressErrors: {enumerable: false}
});
thisProto._gettersByProperty[this.name] =
thisProto._gettersByProperty[this.name] || {};
const interfacingProperties = new proto();
for (let key in interfacingProperties) {
thisProto._gettersByProperty[this.name][key] = get;
Object.defineProperty(thisProto, key, {
enumerable: true,
configurable: true,
get: function() {
const __thisproto__ = Object.getPrototypeOf(this);
try {
const value = get.call(this, key);
if (__thisproto__._valueOverrides[key]) {
return __thisproto__._valueOverrides[key].call(this, value);
}
return value;
} catch (e) {
const __ctrproto__ = Object.getPrototypeOf(this.constructor);
if (!__ctrproto__._suppressErrors) console.error(e.stack);
return null;
}
}
});
}
}
static _setValueOverride(k, fn) {
if (!this.prototype.hasOwnProperty(k)) {
throw new Error(
`Property '${k}' is not defined for ${this.constructor.name}`
);
}
if (typeof fn !== "function") {
return delete this._valueOverrides[k];
}
this.prototype._valueOverrides[k] = fn;
}
static _setSuppressErrors(value) {
Object.getPrototypeOf(this)._suppressErrors = value;
}
// Get a copy of raw model data or property
getRaw(property) {
var allGetters = Object.getPrototypeOf(this)._gettersByProperty;
var getters = allGetters[this.constructor.name];
if (!getters) {
// no own properties, lookup last inherited class
var lastClass = Object.keys(allGetters).pop();
getters = allGetters[lastClass];
}
getters = getters || {};
if (property) {
if (!getters.hasOwnProperty(property)) return;
return getters[property].call(this, property);
}
var copy = {};
for (var key in this) {
try {
if (getters.hasOwnProperty(key))
copy[key] = getters[key].call(this, key);
} catch (e) {
copy[key] = null;
console.error("Could not get key", key);
console.error(e.stack);
}
}
return copy;
}
// Get a copy of internal model data, including inherited properties
toJSON() {
var copy = {};
const __thisproto__ = Object.getPrototypeOf(this);
for (var classname in __thisproto__._gettersByProperty) {
var getters = __thisproto__._gettersByProperty[classname];
for (var key in this) {
try {
if (getters.hasOwnProperty(key)) {
copy[key] = getters[key].call(this, key);
const v = copy[key];
const type = v && v.constructor && v.constructor.name;
if (type === "Set") copy[key] = Array.from(v);
if (type === "Map") copy[key] = Array.from(v.values());
}
} catch (e) {
copy[key] = null;
console.error("Could not get key ", key);
console.error(e.stack);
}
}
if (classname === this.constructor.name) break;
}
return copy;
}
// Custom inspect output (console.log, util.format, util.inspect)
inspect() {
var copy = new (
// create fake object to preserve class name
new Function("return function " + this.constructor.name + "(){}")()
);
for (var key in this) { copy[key] = this[key]; }
return copy;
}
get _valid() {
try {
var allGetters = Object.getPrototypeOf(this)._gettersByProperty;
for (var classname in allGetters) {
if (allGetters[classname].hasOwnProperty("id"))
return allGetters[classname]["id"].call(this, "id");
}
} catch (e) {
return false;
}
}
valueOf() {
if (!this.id) return null;
return this.id;
}
equals(b) {
return this.valueOf() === b.valueOf();
}
/**
* Gets date and time this object was created at.
* @returns {Date}
* @readonly
*/
get createdAt() {
return new Date(Utils.timestampFromSnowflake(this.id));
}
}
module.exports = IBase;

75
node_modules/discordie/lib/interfaces/ICall.js generated vendored Normal file
View File

@ -0,0 +1,75 @@
"use strict";
const Constants = require("../Constants");
const IBase = require("./IBase");
const Utils = require("../core/Utils");
const Call = require("../models/Call");
/**
* @interface
* @model Call
* @extends IBase
*/
class ICall extends IBase {
constructor(discordie, directMessageChannelId) {
super();
Utils.definePrivate(this, {
_discordie: discordie,
_directMessageChannelId: directMessageChannelId
});
Object.freeze(this);
}
/**
* Gets date and time this call was created at.
* @returns {Date}
* @readonly
*/
get createdAt() {
const call = this._discordie._calls.get(this._directMessageChannelId);
if (!call) return new Date(null);
return new Date(Utils.timestampFromSnowflake(call.message_id));
}
/**
* Checks if the call is ringing for current user.
* @return {boolean}
* @readonly
*/
get isRinging() {
const call = this._discordie._calls.get(this._directMessageChannelId);
if (!call) return false;
const userId = this._discordie._user && this._discordie._user.id;
if (!userId) return false;
return call.ringing ? call.ringing.indexOf(userId) >= 0 : false;
}
}
ICall._inherit(Call, function modelPropertyGetter(key) {
return this._discordie._calls.get(this._directMessageChannelId)[key];
});
/**
* @readonly
* @instance
* @memberOf ICall
* @name ringing
* @returns {Array<IUser>|null}
*/
ICall._setValueOverride("ringing", function(ringing) {
const users = [];
if (!ringing) return users;
for (let id of ringing) {
const user = this._discordie.Users.get(id);
if (user) users.push(user);
}
return users;
});
module.exports = ICall;

307
node_modules/discordie/lib/interfaces/IChannel.js generated vendored Normal file
View File

@ -0,0 +1,307 @@
"use strict";
const Constants = require("../Constants");
const ChannelTypes = Constants.ChannelTypes;
const IBase = require("./IBase");
const Utils = require("../core/Utils");
const Channel = require("../models/Channel");
const IUser = require("./IUser");
const IRole = require("./IRole");
const IGuildMember = require("./IGuildMember");
const IAuthenticatedUser = require("./IAuthenticatedUser");
const IPermissions = require("./IPermissions");
const IPermissionOverwrite = require("./IPermissionOverwrite");
const rest = require("../networking/rest");
/**
* @interface
* @model Channel
* @extends IBase
* @description
* Base channel class.
*
* Use the `type` property to determine whether it is a
* `ITextChannel`, `IDirectMessageChannel` or `IVoiceChannel`:
*
* ```js
* Discordie.ChannelTypes: {
* GUILD_TEXT: 0, // ITextChannel
* DM: 1, // IDirectMessageChannel
* GUILD_VOICE: 2, // IVoiceChannel
* GROUP_DM: 3 // IDirectMessageChannel
* }
* ```
*/
class IChannel extends IBase {
constructor(discordie, channelId) {
super();
Utils.definePrivate(this, {
_discordie: discordie,
_channelId: channelId
});
Object.freeze(this);
}
/**
* **Deprecated**: Removed in API v6. Use `isPrivate` instead.
* @return {boolean|null}
* @readonly
*/
get is_private() {
return this.isPrivate;
}
/**
* Checks whether this channel is a direct message channel or a group.
* @return {boolean|null}
* @readonly
*/
get isPrivate() {
if (!this._valid) return null;
return this._discordie._channels._isPrivate(this);
}
/**
* Checks whether this channel is a voice channel in a guild.
* @return {boolean}
* @readonly
*/
get isGuildVoice() {
return this.type === ChannelTypes.GUILD_VOICE;
}
/**
* Checks whether this channel is a text channel in a guild.
* @return {boolean}
* @readonly
*/
get isGuildText() {
return this.type === ChannelTypes.GUILD_TEXT;
}
/**
* Checks whether this channel is a direct message channel (non-group).
* @return {boolean}
* @readonly
*/
get isDM() {
return this.type === ChannelTypes.DM;
}
/**
* Checks whether this channel is a group direct message channel.
* @return {boolean}
* @readonly
*/
get isGroupDM() {
return this.type === ChannelTypes.GROUP_DM;
}
/**
* Gets guild of this channel.
* @returns {IGuild|null}
* @readonly
*/
get guild() {
return this._discordie.Guilds.get(this.guild_id);
}
/**
* Makes a request to create an invite for this channel.
* @param {Object} options
* @returns {Promise<Object, Error>}
* @example
* channel.createInvite({
* max_age: 60 * 60 * 24, // value in seconds
* max_uses: 0, // pretty obvious
* temporary: false
* // temporary membership, kicks members without roles on disconnect
* });
* // Example response:
* {
* "max_age": 86400,
* "code": "AAAAAAAAAAAAAAAA",
* "guild": {
* "splash_hash": null,
* "id": "00000000000000000",
* "name": "test"
* },
* "revoked": false,
* "created_at": "2015-10-16T10:45:38.566978+00:00",
* "temporary": false,
* "uses": 0,
* "max_uses": 0,
* "inviter": {
* "username": "testuser",
* "discriminator": "3273",
* "bot": true,
* "id": "00000000000000000",
* "avatar": null
* },
* "channel": {
* "type": "text",
* "id": "000000000000000000",
* "name": "testchannel"
* }
* }
*/
createInvite(options) {
return this._discordie.Invites.create(this, options);
}
/**
* Makes a request to create a permission overwrite for this channel.
* @param {IAuthenticatedUser|IRole|IGuildMember} roleOrMember
* @param {IPermissions|Number} [allow]
* @param {IPermissions|Number} [deny]
* @returns {Promise<IPermissionOverwrite, Error>}
* @example
* channel.createPermissionOverwrite(this.User);
* channel.createPermissionOverwrite(
* channel.guild.members.find(m => m.username == "testuser")
* );
* channel.createPermissionOverwrite(
* channel.guild.roles.find(r => r.name == "new role")
* )
* .then(overwrite => console.log(overwrite))
* .catch(error => console.log(error));
*/
createPermissionOverwrite(roleOrMember, allow, deny) {
if (![IAuthenticatedUser, IRole, IGuildMember]
.some(t => roleOrMember instanceof t))
throw new TypeError(
"roleOrMember must be an instance of IRole or IGuildMember"
);
if (allow === undefined) allow = 0;
if (deny === undefined) deny = 0;
if (allow instanceof IPermissions) allow = allow.raw;
if (deny instanceof IPermissions) deny = deny.raw;
const types = [
{t: IRole, s: "role"},
{t: IGuildMember, s: "member"},
{t: IAuthenticatedUser, s: "member"}
];
const type = types.find(spec => roleOrMember instanceof spec.t).s;
return new Promise((rs, rj) => {
const raw = {
id: roleOrMember.valueOf(),
type: type,
allow: allow,
deny: deny
};
rest(this._discordie)
.channels.putPermissionOverwrite(this._channelId, raw)
.then(o =>
rs(new IPermissionOverwrite(this._discordie, o.id, this._channelId))
)
.catch(rj);
});
}
/**
* Makes a request to update this channel.
* @param {String} [name]
* @param {String} [topic]
* @param {Number} [bitrate] - Only for voice channels
* @param {Number} [userLimit] - Only for voice channels
* @returns {Promise<IChannel, Error>}
*/
update(name, topic, bitrate, userLimit) {
if (typeof name !== "string") name = this.name;
if (typeof topic !== "string") topic = this.topic;
if (typeof bitrate !== "number") bitrate = this.bitrate;
if (typeof userLimit !== "number") userLimit = this.user_limit;
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.patchChannel(this.id,
name, topic, bitrate, userLimit, this.position
)
.then((channel) => rs(this._discordie.Channels.get(channel.id)))
.catch(rj);
});
}
/**
* Makes a request to create a new channel with permission overwrites of
* this channel.
* @param {String} name
* @param {Number} [type] - See IChannel / Discordie.ChannelTypes
* @param {Number} [bitrate] - Only for voice channels
* @param {Number} [userLimit] - Only for voice channels
*/
clone(name, type, bitrate, userLimit) {
if (!this._valid) return Promise.reject(new Error("Invalid channel"));
if (!this.guild) return Promise.reject(new Error("Invalid guild"));
name = name != null ? name : this.name;
type = type != null ? type : this.type;
if (type === ChannelTypes.GUILD_VOICE || type === "voice") {
if (bitrate == null) bitrate = this.bitrate;
if (userLimit == null) userLimit = this.user_limit;
}
const permissionOverwrites = this.permission_overwrites;
return this.guild
.createChannel(type, name, permissionOverwrites, bitrate, userLimit);
}
/**
* Moves this channel to `position` and makes a batch channel update request.
* @param {Number} position
* @returns {Promise}
*/
setPosition(position) {
const channels = (this.type == ChannelTypes.GUILD_VOICE) ?
this.guild.voiceChannels :
this.guild.textChannels;
const changes = Utils.reorderObjects(channels, this, position);
if (!changes) return Promise.resolve();
return rest(this._discordie)
.channels.batchPatchChannels(this.guild_id, changes);
}
/**
* Makes a request to delete this channel.
* @returns {Promise}
*/
delete() {
return rest(this._discordie).channels.deleteChannel(this.id);
}
/**
* Makes a request to get a list of invites for this channel.
* @returns {Promise<Array<Object>, Error>}
*/
getInvites() {
return rest(this._discordie).channels.getInvites(this.id);
}
}
IChannel._inherit(Channel, function modelPropertyGetter(key) {
return this._discordie._channels.get(this._channelId)[key];
});
/**
* @readonly
* @instance
* @memberOf IChannel
* @name permission_overwrites
* @returns {Array<IPermissionOverwrite>}
*/
IChannel._setValueOverride("permission_overwrites", function(overwritesRaw) {
const overwrites = [];
if (!overwritesRaw) return overwrites;
for (let overwrite of overwritesRaw) {
overwrites.push(new IPermissionOverwrite(
this._discordie, overwrite.id, this._channelId
));
}
return overwrites;
});
module.exports = IChannel;

View File

@ -0,0 +1,65 @@
"use strict";
const ICollectionBase = require("./ICollectionBase");
const IChannel = require("./IChannel");
const ITextChannel = require("./ITextChannel");
const IVoiceChannel = require("./IVoiceChannel");
const IGuild = require("./IGuild");
const Utils = require("../core/Utils");
const Constants = require("../Constants");
const ChannelTypes = Constants.ChannelTypes;
/**
* @interface
* @extends ICollectionBase
*/
class IChannelCollection extends ICollectionBase {
constructor(discordie, valuesGetter, valueGetter) {
super({
valuesGetter: valuesGetter,
valueGetter: valueGetter,
itemFactory: (id) => {
const type = this._discordie._channels.getChannelType(id);
if (type && type === ChannelTypes.GUILD_VOICE) {
return new IVoiceChannel(this._discordie, id);
}
return new ITextChannel(this._discordie, id);
}
});
Utils.definePrivate(this, {_discordie: discordie});
}
/**
* Creates an array of `IChannel` (`ITextChannel` and `IVoiceChannel`)
* for `guild`.
* @param {IGuild|String} guild
* @returns {Array<IChannel>}
*/
forGuild(guild) {
return this.filter(channel => channel.guild_id == guild.valueOf());
}
/**
* Creates an array of `ITextChannel` for `guild`.
* @param {IGuild|String} guild
* @returns {Array<ITextChannel>}
*/
textForGuild(guild) {
return this.filter(channel =>
channel.guild_id == guild && channel.type == ChannelTypes.GUILD_TEXT
);
}
/**
* Creates an array of `IVoiceChannel` for `guild`.
* @param {IGuild|String} guild
* @returns {Array<IVoiceChannel>}
*/
voiceForGuild(guild) {
return this.filter(channel =>
channel.guild_id == guild && channel.type == ChannelTypes.GUILD_VOICE
);
}
}
module.exports = IChannelCollection;

View File

@ -0,0 +1,192 @@
"use strict";
const Utils = require("../core/Utils");
class ICollectionBase {
constructor(descriptor) {
if (!descriptor.valuesGetter)
throw new Error("valuesGetter is not defined");
if (!descriptor.itemFactory)
throw new Error("itemFactory is not defined");
this._valuesGetter = descriptor.valuesGetter;
this._valueGetter = descriptor.valueGetter;
this._itemFactory = descriptor.itemFactory;
this._cache = new WeakMap();
Utils.privatify(this);
}
_getOrCreateInterface(item, customItemFactory) {
var factory = customItemFactory || this._itemFactory;
var cache = this._cache;
if (!cache.get(item))
cache.set(item, factory(item.id));
return cache.get(item);
}
/**
* Returns an an element, if `key` of an element in the collection
* with exact `value` can be found.
* Otherwise null is returned.
* @param key
* @param value
* @returns {*}
*/
getBy(key, value) {
if (key === "id" && this._valueGetter) {
var item = this._valueGetter(value);
if (!item) return null;
return this._getOrCreateInterface(item);
}
for (var item of this._valuesGetter()) {
if (item[key] != value) continue;
return this._getOrCreateInterface(item);
}
return null;
}
/**
* Returns an element with requested `id`, if exists in the collection.
* Otherwise null is returned.
* @param {String} id
* @returns {*}
*/
get(id) { return this.getBy("id", id); }
*[Symbol.iterator]() {
for (var item of this._valuesGetter()) {
yield this._getOrCreateInterface(item);
}
}
_getRawItemBy(key, value) {
for (var item of this._valuesGetter()) {
if (item[key] != value) continue;
return item;
}
}
*_getRawIterator() {
for (var item of this._valuesGetter()) {
yield item;
}
}
_getRaw(id) { return this._getRawItemBy("id", id); }
/**
* Creates a new array with all elements that pass the test implemented
* by the provided function.
* @param {Function} fn - Function with signature fn(item)
* @returns {Array}
*/
filter(condition) {
if (typeof condition !== "function") {
throw new TypeError("condition is not a function");
}
const items = [];
for (var item of this) {
if (condition(item))
items.push(item);
}
return items;
}
concat(collection) {
if (collection == null) {
throw new TypeError("collection is null or not defined");
}
if (typeof collection.filter !== "function") {
throw new TypeError();
}
collection = collection.filter(() => true);
return this.filter(() => true).concat(collection);
}
/**
* Returns a value in the collection, if an element in the collection
* satisfies the provided testing function. Otherwise null is returned.
* @param {Function} fn - Function with signature fn(item)
* @returns {Object|null}
*/
find(condition) {
if (typeof condition !== "function") {
throw new TypeError("condition is not a function");
}
for (var item of this) {
if (condition(item))
return item;
}
return null;
}
/**
* Executes a provided function once per element.
* @param {Function} fn - Function with signature fn(item)
*/
forEach(fn) {
if (typeof fn !== "function") {
throw new TypeError("fn is not a function");
}
for (var item of this) {
fn(item);
}
}
/**
* Creates a new array with the results of calling a provided function on
* every element in this collection.
* @param {Function} fn - Function with signature fn(item)
* @returns {Array}
*/
map(fn) {
if (typeof fn !== "function") {
throw new TypeError("fn is not a function");
}
const items = [];
for (var item of this) {
items.push(fn(item));
}
return items;
}
inspect() {
var copy = new (
// create fake object to preserve class name
new Function("return function " + this.constructor.name + "(){}")()
);
copy.length = this.length;
return copy;
}
/**
* Creates a new array with elements of this collection.
* @returns {Array}
*/
toArray() {
const array = [];
for (var item of this) {
array.push(item);
}
return array;
}
toJSON() { return this.toArray(); }
/**
* Number of elements in this collection.
* @returns {Number}
* @readonly
*/
get length() {
var i = 0;
for (var item of this._valuesGetter()) i++;
return i;
}
/**
* Number of elements in this collection. Alias for `.length`.
* @returns {Number}
* @readonly
*/
get size() { return this.length; }
}
module.exports = ICollectionBase;

View File

@ -0,0 +1,589 @@
"use strict";
const Constants = require("../Constants");
const Endpoints = Constants.Endpoints;
const ChannelTypes = Constants.ChannelTypes;
const IBase = require("./IBase");
const ITextChannel = require("./ITextChannel");
const ICall = require("./ICall");
const Utils = require("../core/Utils");
const Channel = require("../models/Channel");
const rest = require("../networking/rest");
/**
* @interface
* @model Channel
* @extends IBase
*/
class IDirectMessageChannel extends IBase {
constructor(discordie, directMessageChannelId) {
super();
Utils.definePrivate(this, {
_discordie: discordie,
_directMessageChannelId: directMessageChannelId,
_call: new ICall(discordie, directMessageChannelId)
});
Object.freeze(this);
}
/**
* **Deprecated**: Removed in API v6. Use `isPrivate` instead.
* @return {boolean|null}
* @readonly
*/
get is_private() {
return this.isPrivate;
}
/**
* Checks whether this channel is a direct message channel or a group.
* @return {boolean|null}
* @readonly
*/
get isPrivate() {
if (!this._valid) return null;
return this._discordie._channels._isPrivate(this);
}
/**
* Checks whether this channel is a voice channel in a guild.
* @return {boolean}
* @readonly
*/
get isGuildVoice() {
return this.type === ChannelTypes.GUILD_VOICE;
}
/**
* Checks whether this channel is a text channel in a guild.
* @return {boolean}
* @readonly
*/
get isGuildText() {
return this.type === ChannelTypes.GUILD_TEXT;
}
/**
* Checks whether this channel is a direct message channel (non-group).
* @return {boolean}
* @readonly
*/
get isDM() {
return this.type === ChannelTypes.DM;
}
/**
* Checks whether this channel is a group direct message channel.
* @return {boolean}
* @readonly
*/
get isGroupDM() {
return this.type === ChannelTypes.GROUP_DM;
}
/**
* Returns the owner of the private channel.
*
* Returns null if the owner user is not in cache or there is no owner.
* @returns {IAuthenticatedUser|IUser|null}
* @readonly
*/
get owner() {
if (!this.owner_id) return null;
const owner = this._discordie.Users.get(this.owner_id);
if (!owner) return null;
if (this._discordie.User.equals(owner))
return this._discordie.User;
return owner;
}
/**
* Checks whether the `user` is the owner of the private channel.
* @param {IGuildMember|IUser|IAuthenticatedUser|String} user
* @returns {boolean}
*/
isOwner(user) {
if (!user) return false;
if (!this.owner_id) return false;
return this.owner_id === user.valueOf();
}
/**
* Creates a string URL of image icon of this channel.
* @returns {String|null}
* @readonly
*/
get iconURL() {
if (!this.icon) return null;
return Constants.CDN_ENDPOINT + Endpoints.CDN_DM_ICON(this.id, this.icon);
}
/**
* Gets first recipient of this channel.
*
* Returns null if this channel is invalid or has no recipients.
*
* **Deprecated**: Use `recipients` instead.
* @returns {IUser|null}
* @readonly
*/
get recipient() {
if (!this._valid) return null;
return this.recipients[0] || null;
}
/**
* Gets a value indicating whether all messages were loaded.
* @returns {boolean}
* @readonly
*/
get allMessagesLoaded() {
return !this._discordie._messages.channelHasMore(this.id);
}
/**
* Creates an array of cached messages in this channel, sorted in order of
* arrival (message cache is sorted on message insertion, not when this
* getter is invoked).
*
* Returns an empty array if channel no longer exists.
* @returns {Array<IMessage>}
* @readonly
*/
get messages() {
return this._discordie.Messages.forChannel(this.id);
}
/**
* Makes a request to fetch messages for this channel.
*
* Discord API does not allow fetching more than 100 messages at once.
*
* Promise resolves with an Object with following structure:
* ```js
* {
* messages: Array<IMessage>,
* limit: Number, // same as parameter passed or default value
* before: String | null, // message id
* after: String | null // message id
* }
* ```
* @param {Number|null} [limit] - Default is 100
* @param {IMessage|String|null} [before] - Message or message id
* @param {IMessage|String|null} [after] - Message or message id
* @returns {Promise<Object, Error>}
* @example
* var guild = client.Guilds.find(g => g.name == "test");
* var channel = guild.generalChannel;
*
* // simple fetch:
* channel.fetchMessages().then(() => {
* console.log("[simple] messages in cache: " + channel.messages.length);
* });
*
* // fetching more than 100 messages into cache sequentially:
* fetchMessagesEx(channel, 420).then(() => {
* console.log("[extended] messages in cache: " + channel.messages.length);
* });
*
* // fetch more messages just like Discord client does
* function fetchMessagesEx(channel, left) {
* // message cache is sorted on insertion
* // channel.messages[0] will get oldest message
* var before = channel.messages[0];
* return channel.fetchMessages(Math.min(left, 100), before)
* .then(e => onFetch(e, channel, left));
* }
* function onFetch(e, channel, left) {
* if (!e.messages.length) return Promise.resolve();
* left -= e.messages.length;
* console.log(`Received ${e.messages.length}, left: ${left}`);
* if (left <= 0) return Promise.resolve();
* return fetchMessagesEx(channel, left);
* }
*/
fetchMessages(limit, before, after) {
return ITextChannel.prototype.fetchMessages.apply(this, arguments);
}
/**
* Creates an array of cached pinned messages in this channel.
*
* Pinned message cache is updated only if all pinned messages have been
* loaded with `IDirectMessageChannel.fetchPinned()`.
*
* Returns an empty array if channel no longer exists or if pinned messages
* have not been fetched yet.
* @returns {Array<IMessage>}
* @readonly
*/
get pinnedMessages() {
return this._discordie.Messages.forChannelPinned(this.id);
}
/**
* Makes a request to fetch pinned messages for this channel.
*
* Promise resolves with an Object with following structure:
* ```js
* {
* channelId: String,
* messages: Array<IMessage>
* }
* ```
* @returns {Promise<Object, Error>}
*/
fetchPinned() {
return new Promise((rs, rj) => {
rest(this._discordie).channels.getPinnedMessages(this.id)
.then(e => {
e.messages = e.messages
.map(msg => this._discordie.Messages.get(msg.id));
return rs(e);
})
.catch(rj);
});
}
/**
* Makes a request to send a message to this channel. Messages over 2000
* characters will be rejected by the server.
*
* Use `uploadFile` if you want to send a message with an attachment.
* @param {String|Array<String>} content
* Strings will be sent as is, arrays - joined with a newline character.
* @param {IUser|IGuildMember|Array<IUser>|Array<IGuildMember>} [mentions]
* Deprecated: left for backward compatibility.
* @param {boolean} [tts]
* @param {Object} [embed]
* Refer to [official API documentation](https://discordapp.com/developers/docs/resources/channel#embed-object)
* for embed structure description.
* @returns {Promise<IMessage, Error>}
* @example
* var guild = client.Guilds.find(g => g.name == "test");
* if (!guild) return console.log("invalid guild");
*
* var channel = guild.generalChannel;
*
* channel.sendMessage("regular message");
* channel.sendMessage("test with tts", true);
*
* var user = client.Users.find(u => u.username == "test");
* if (!user) return console.log("invalid user");
*
* channel.sendMessage("mentioning user " + user.mention);
* channel.sendMessage("@everyone or @here mention if you have permissions");
*
* channel.sendMessage("message with an embed", false, {
* color: 0x3498db,
* author: {name: "author name"},
* title: "This is an embed",
* url: "http://google.com",
* timestamp: "2016-11-13T03:43:32.127Z",
* fields: [{name: "some field", value: "some value"}],
* footer: {text: "footer text"}
* });
*/
sendMessage(content, mentions, tts, embed) {
return ITextChannel.prototype.sendMessage.apply(this, arguments);
}
/**
* Makes a request to upload data to this channel.
* Images require a `filename` with a valid extension to actually be uploaded.
* @param {Buffer|ReadableStream|String} readableStream
* Data to upload or filename as a string
* @param {String} filename
* Actual filename to show, required for non-string `readableStream`
* @param {String} [content] - Additional comment message for attachment
* @param {boolean} [tts]
* @returns {Promise<IMessage, Error>}
* @example
* channel.uploadFile(fs.readFileSync("test.png"), "test.png"); // Buffer
* channel.uploadFile(fs.createReadStream("test.png"), "test.png"); // Stream
* channel.uploadFile("test.png"); // File
* channel.uploadFile("test.png", null, "file with message");
* channel.uploadFile("test.png", null, "file with message and tts", true);
*/
uploadFile(readableStream, filename, content, mentions, tts) {
return ITextChannel.prototype.uploadFile.apply(this, arguments);
}
/**
* Makes a request to send typing status for this channel.
*
* Discord client displays it for 10 seconds, sends every 5 seconds.
* Stops showing typing status if receives a message from the user.
* @returns {Promise}
*/
sendTyping() {
return ITextChannel.prototype.sendTyping.apply(this, arguments);
}
/**
* Makes a request to close this channel (direct message channels only).
* @returns {Promise}
*/
close() {
return rest(this._discordie).channels.deleteChannel(this.id);
}
/**
* Makes a request to ring specified recipients.
* Has no effect if call has not started yet.
*
* Bot accounts cannot use this endpoint.
* @param {Array<IUser|String>} [recipients]
* @return {Promise}
*/
ring(recipients) {
if (recipients && !Array.isArray(recipients))
throw new TypeError("Param 'recipients' is not an array");
if (recipients) recipients = recipients.map(u => u.valueOf());
return rest(this._discordie).channels.calls
.ring(this.id, recipients);
}
/**
* Makes a request to decline an incoming call (if no arguments passed) or
* stop ringing for specified recipients.
*
* Bot accounts cannot use this endpoint.
* @param {Array<IUser|String>} [recipients]
* @return {Promise}
*/
stopRinging(recipients) {
if (recipients && !Array.isArray(recipients))
throw new TypeError("Param 'recipients' is not an array");
if (recipients) recipients = recipients.map(u => u.valueOf());
return rest(this._discordie).channels.calls
.stopRinging(this.id, recipients);
}
/**
* Makes a request to change the server region hosting the call.
*
* Bot accounts cannot use this endpoint.
* @param {String} region
* @return {Promise}
*/
changeCallRegion(region) {
return rest(this._discordie).channels.calls.changeRegion(this.id, region);
}
/**
* Makes a request to add a user to this private channel.
*
* Bot accounts cannot use this endpoint.
* @param {IUser|IGuildMember} user
* @return {Promise}
*/
addRecipient(user) {
user = user.valueOf();
return rest(this._discordie).channels.dm.addRecipient(this.id, user);
}
/**
* Makes a request to remove a user from this private channel.
*
* Bot accounts cannot use this endpoint.
* @param {IUser|IGuildMember} user
* @return {Promise}
*/
removeRecipient(user) {
user = user.valueOf();
return rest(this._discordie).channels.dm.removeRecipient(this.id, user);
}
/**
* Makes a request to set a name for this private channel.
* @param {String} name
* @return {Promise<IDirectMessageChannel, Error>}
*/
setName(name) {
return new Promise((rs, rj) => {
rest(this._discordie).channels.dm.setName(this.id, name)
.then(() => rs(this))
.catch(rj);
});
}
/**
* Makes a request to set an icon for this private channel.
* @param {String|Buffer|null} icon
* @return {Promise<IDirectMessageChannel, Error>}
*/
setIcon(icon) {
if (icon instanceof Buffer) {
icon = Utils.imageToDataURL(icon);
} else if (icon === undefined) {
icon = this.icon;
}
return new Promise((rs, rj) => {
rest(this._discordie).channels.dm.setIcon(this.id, icon)
.then(() => rs(this))
.catch(rj);
});
}
/**
* Creates or joins a call.
* Only one call can be connected at the same time.
*
* Joining calls with bot accounts is not supported.
* @param {boolean} [selfMute]
* @param {boolean} [selfDeaf]
* @returns {Promise<VoiceConnectionInfo, Error|Number>}
*/
joinCall(selfMute, selfDeaf) {
selfMute = !!selfMute;
selfDeaf = !!selfDeaf;
if (!this._valid)
return Promise.reject(new Error("Channel does not exist"));
const call = this._discordie._calls.get(this._directMessageChannelId);
if (call && call.unavailable)
return Promise.reject(new Error("Call is unavailable"));
if (this._discordie._user && this._discordie._user.bot)
throw new Error("Joining calls with bot accounts is not supported");
const vc = this._discordie.VoiceConnections;
return vc._getOrCreate(null, this.id, selfMute, selfDeaf);
}
/**
* Leaves call if joined.
*/
leaveCall() {
const info = this.getVoiceConnectionInfo();
if (info) return info.voiceConnection.disconnect();
this._discordie.VoiceConnections
.cancelIfPending(null, this._directMessageChannelId);
}
/**
* Retrieves `VoiceConnectionInfo` for the call of this channel.
* @returns {VoiceConnectionInfo|null}
*/
getVoiceConnectionInfo() {
return this._discordie.VoiceConnections
.getForChannel(this._directMessageChannelId);
}
/**
* Checks whether current user is in the call.
* @returns {boolean}
* @readonly
*/
get joinedCall() {
const vc = this._discordie.VoiceConnections;
const pendingChannel = vc.getPendingChannel(null);
const channelId = this._directMessageChannelId;
return !!(pendingChannel && pendingChannel === channelId);
}
/**
* Creates an array of users in the call.
*
* Returns null if call does not exist in cache or has not started yet.
* @returns {Array<IUser>|null}
* @readonly
*/
get usersInCall() {
const call = this._discordie._calls.get(this._directMessageChannelId);
return this._discordie.Users.usersInCall(this);
}
/**
* Gets call from cache.
*
* Returns null if call does not exist in cache or has not started yet.
* @returns {ICall|null}
* @readonly
*/
get call() {
const call = this._discordie._calls.get(this._directMessageChannelId);
return call ? this._call : null;
}
/**
* Fetches call info through gateway socket.
*
* Currently there are no ways to fetch call info for all channels at once.
* @return {Promise<ICall|null, Error>}
*/
fetchCall() {
const gateway = this._discordie.gatewaySocket;
if (!gateway || !gateway.connected)
return Promise.reject(new Error("No gateway socket (not connected)"));
return new Promise((rs, rj) => {
const call = this._discordie._calls.get(this._directMessageChannelId);
if (call) return rs(this._call);
gateway.callConnect(this._directMessageChannelId);
setTimeout(() => {
const call = this._discordie._calls.get(this._directMessageChannelId);
rs(call ? this._call : null);
}, 1000);
});
}
}
IDirectMessageChannel._inherit(Channel, function modelPropertyGetter(key) {
return this._discordie._channels.get(this._directMessageChannelId)[key];
});
/**
* @readonly
* @instance
* @memberOf IDirectMessageChannel
* @name name
* @returns {String}
*/
IDirectMessageChannel._setValueOverride("name", function(name) {
const UNNAMED = "Unnamed";
if (!this._valid) return UNNAMED;
const type = this.type;
if (type === ChannelTypes.DM) {
const recipient = this.recipients[0];
return recipient ? recipient.username : name;
}
if (type === ChannelTypes.GROUP_DM) {
return name || this.recipients.map(u => u.username).join(", ") || UNNAMED;
}
return name;
});
/**
* Gets recipients of this channel.
* @readonly
* @instance
* @memberOf IDirectMessageChannel
* @name recipients
* @returns {Array<IUser>|null}
*/
IDirectMessageChannel._setValueOverride("recipients", function(recipients) {
const users = [];
if (!recipients) return users;
for (let id of recipients.values()) {
const user = this._discordie.Users.get(id);
if (user) users.push(user);
}
return users;
});
module.exports = IDirectMessageChannel;

View File

@ -0,0 +1,72 @@
"use strict";
const ICollectionBase = require("./ICollectionBase");
const IDirectMessageChannel = require("./IDirectMessageChannel");
const Utils = require("../core/Utils");
const Constants = require("../Constants");
const ChannelTypes = Constants.ChannelTypes;
const rest = require("../networking/rest");
/**
* @interface
* @extends ICollectionBase
*/
class IDirectMessageChannelCollection extends ICollectionBase {
constructor(discordie, valuesGetter, valueGetter) {
super({
valuesGetter: valuesGetter,
valueGetter: valueGetter,
itemFactory: (id) => new IDirectMessageChannel(this._discordie, id)
});
this._discordie = discordie;
Utils.privatify(this);
}
/**
* Gets a DM channel from cache or makes a request to create one.
* @param {IUser|IGuildMember|String} recipient
* @returns {Promise<IDirectMessageChannel, Error>}
*/
getOrOpen(recipient) {
const existing = this.find(c =>
c.type === ChannelTypes.DM &&
c.recipients.length === 1 &&
c.recipients[0].equals(recipient)
);
if (existing)
return Promise.resolve(existing);
return this.open(recipient);
}
/**
* Makes a request to create a DM channel.
* @param {IUser|IGuildMember|String} recipient
* @returns {Promise<IDirectMessageChannel, Error>}
*/
open(recipient) {
recipient = recipient.valueOf();
return this.createGroupDM([recipient]);
}
/**
* Makes a request to create a group DM channel.
*
* Bot accounts cannot use this endpoint.
* @param {Array<IUser|IGuildMember|String>} [recipients]
* @returns {Promise<IDirectMessageChannel, Error>}
*/
createGroupDM(recipients) {
recipients = recipients || [];
recipients = recipients.filter(u => u).map(u => u.valueOf());
const userId = this._discordie.User.id;
return new Promise((rs, rj) => {
rest(this._discordie)
.users.createDirectMessageChannel(userId, recipients)
.then(c => rs(this._discordie.DirectMessageChannels.get(c.id)))
.catch(rj);
});
}
}
module.exports = IDirectMessageChannelCollection;

542
node_modules/discordie/lib/interfaces/IGuild.js generated vendored Normal file
View File

@ -0,0 +1,542 @@
"use strict";
const Constants = require("../Constants");
const Endpoints = Constants.Endpoints;
const IBase = require("./IBase");
const IChannel = require("./IChannel");
const IGuildMember = require("./IGuildMember");
const IUser = require("./IUser");
const IRole = require("./IRole");
const Utils = require("../core/Utils");
const Guild = require("../models/Guild");
const rest = require("../networking/rest");
/**
* @interface
* @model Guild
* @extends IBase
*/
class IGuild extends IBase {
constructor(discordie, guildId) {
super();
Utils.definePrivate(this, {
_discordie: discordie,
_guildId: guildId
});
Object.freeze(this);
}
/**
* Creates an acronym string for this guild.
* (Text that shows up as guild icon in the client if there is no image icon.)
* @returns {String}
* @readonly
*/
get acronym() {
if (!this.name) return "";
return this.name.replace(/\w+/g, match => match[0]).replace(/\s/g, "");
}
/**
* Creates a string URL of image icon of this guild.
* @returns {String|null}
* @readonly
*/
get iconURL() {
if (!this.icon) return null;
return Constants.CDN_ENDPOINT +
Endpoints.CDN_GUILD_ICON(this.id, this.icon);
}
/**
* Creates a string URL of invite splash image of this guild.
* @returns {String|null}
* @readonly
*/
get splashURL() {
if (!this.splash) return null;
return Constants.CDN_ENDPOINT +
Endpoints.CDN_GUILD_SPLASH(this.id, this.splash);
}
/**
* Checks whether the `user` is the owner of this guild.
* @param {IGuildMember|IUser|IAuthenticatedUser|String} user
* @returns {boolean}
*/
isOwner(user) {
if (!user) return false;
if (!this.owner_id) return false;
return this.owner_id === user.valueOf();
}
/**
* Returns afk channel of this guild.
* @returns {IChannel|null}
* @readonly
*/
get afk_channel() {
if (!this.afk_channel_id) return null;
const afk_channel = this._discordie.Channels.get(this.afk_channel_id);
return afk_channel ? afk_channel : null;
}
/**
* Returns the owner of this guild.
*
* Returns null if the owner user is not in cache.
*
* See `.isOwner(user)` if you want to safely check if the `user` is owner.
* @returns {IAuthenticatedUser|IUser|null}
* @readonly
*/
get owner() {
if (!this.owner_id) return null;
const owner = this._discordie.Users.get(this.owner_id);
if (!owner) return null;
if (this._discordie.User.equals(owner))
return this._discordie.User;
return owner;
}
/**
* Creates an array of text and voice channels of this guild.
* @returns {Array<IChannel>}
* @readonly
*/
get channels() {
return this._discordie.Channels.forGuild(this.id);
}
/**
* Creates an array of text channels of this guild.
* @returns {Array<ITextChannel>}
* @readonly
*/
get textChannels() {
return this._discordie.Channels.textForGuild(this.id);
}
/**
* Creates an array of voice channels of this guild.
* @returns {Array<IVoiceChannel>}
* @readonly
*/
get voiceChannels() {
return this._discordie.Channels.voiceForGuild(this.id);
}
/**
* Returns general channel of this guild.
* @returns {ITextChannel}
* @readonly
*/
get generalChannel() {
return this._discordie.Channels.get(this.id);
}
/**
* Makes a request to edit this guild,
* substituting `undefined` or `null` properties with current values.
*
* Passing `null` in `icon` or `afkChannelId` will remove current
* icon/channel. Use `undefined` instead of `null` in this case.
* @param {String} [name]
* @param {String|Buffer|null} [icon]
* @param {String} [region]
* @param {IChannel|String|null} [afkChannelId] - Channel or an id string
* @param {Number} [afkTimeout] - 60, 300, 900, 1800 or 3600 seconds
* @param {Number} [verificationLevel]
* See Discordie.VerificationLevel
* @param {Number} [defaultMessageNotifications]
* See Discordie.UserNotificationSettings
* @returns {Promise<IGuild, Error>}
*/
edit(name, icon, region, afkChannelId, afkTimeout, verificationLevel,
defaultMessageNotifications) {
const guild = this._discordie._guilds.get(this._guildId);
name = name || guild.name;
region = region || guild.region;
afkTimeout = afkTimeout || guild.afk_timeout;
const opt = (value, _) => value != null ? value : _;
verificationLevel =
opt(verificationLevel, guild.verification_level);
defaultMessageNotifications =
opt(defaultMessageNotifications, guild.default_message_notifications);
if (icon instanceof Buffer) {
icon = Utils.imageToDataURL(icon);
} else if (icon === undefined) {
icon = guild.icon;
}
if (afkChannelId === undefined) {
afkChannelId = guild.afk_channel_id;
} else if (afkChannelId != null) {
afkChannelId = afkChannelId.valueOf();
}
return new Promise((rs, rj) => {
rest(this._discordie)
.guilds.patchGuild(
guild.id,
name, icon, region,
afkChannelId, afkTimeout,
verificationLevel,
defaultMessageNotifications
)
.then(() => rs(this))
.catch(rj);
});
}
/**
* Makes a request to create a channel in this guild.
* @param {Number} type - See IChannel / Discordie.ChannelTypes
* @param {String} name
* @param {Array<IPermissionOverwrite>} [permissionOverwrites]
* @param {Number} [bitrate] - Only for voice channels
* @param {Number} [userLimit] - Only for voice channels
* @returns {Promise<ITextChannel|IVoiceChannel, Error>}
*/
createChannel(type, name, permissionOverwrites, bitrate, userLimit) {
permissionOverwrites = permissionOverwrites || [];
if (!Array.isArray(permissionOverwrites))
throw TypeError("Param 'permissionOverwrites' must be an array");
permissionOverwrites = permissionOverwrites.map(v => {
return typeof v.getRaw === "function" ? v.getRaw() : null;
}).filter(v => v);
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.createChannel(this.id,
type, name, permissionOverwrites, bitrate, userLimit
)
.then(channel => rs(this._discordie.Channels.get(channel.id)))
.catch(rj);
});
}
/**
* Makes a request to create a role in this guild.
* @returns {Promise<IRole, Error>}
*/
createRole() {
return new Promise((rs, rj) => {
rest(this._discordie).guilds.roles.createRole(this.id)
.then(role => rs(new IRole(this._discordie, role.id, this.id)))
.catch(rj);
});
}
/**
* Makes a request to create an invite for general channel in this guild.
*
* See `IChannel.createInvite` for more info.
* @see IChannel.createInvite
* @param {Object} options
* @returns {Promise<Object, Error>}
*/
createInvite(options) {
return this._discordie.Invites.create(this.generalChannel, options);
}
/**
* Creates an array containing members of this guild.
* @returns {Array<IGuildMember>}
* @readonly
*/
get members() {
return this._discordie.Users.membersForGuild(this.id);
}
/**
* Makes a request to delete this guild.
*
* Returns a rejected promise if the user **is not** owner.
* @returns {Promise}
*/
delete() {
if (!this.owner.equals(this._discordie.User))
return Promise.reject();
return rest(this._discordie).guilds.deleteGuild(this.id);
}
/**
* Makes a request to delete this guild.
*
* Returns a rejected promise if the user **is** owner.
* @returns {Promise}
*/
leave() {
if (this.owner.equals(this._discordie.User))
return Promise.reject();
return rest(this._discordie).guilds.leaveGuild(this.id);
}
/**
* Makes a request to ban a user (does not have to be a member of the guild).
*
* Additionally delete `deleteMessageForDays` number of days worth of their
* messages from all channels of the guild.
* @param {IUser|String} user - User or an id string
* @param deleteMessageForDays - Number of days worth of messages to delete
* (min 0, max 7)
* @returns {Promise}
*/
ban(user, deleteMessageForDays) {
user = user.valueOf();
return rest(this._discordie)
.guilds.bans.banMember(this.id, user, deleteMessageForDays);
}
/**
* Makes a request to unban a user.
* @param {IUser|String} user - User or an id string
* @returns {Promise}
*/
unban(user) {
user = user.valueOf();
return rest(this._discordie).guilds.bans.unbanMember(this.id, user);
}
/**
* Makes a request to get ban list of this guild.
* @returns {Promise<Array<IUser>, Error>}
*/
getBans() {
return new Promise((rs, rj) => {
rest(this._discordie).guilds.bans.getBans(this.id)
.then(bans => {
const bannedUsers = bans.map(ban => {
return new IUser(this._discordie, ban.user.id);
});
return rs(bannedUsers);
})
.catch(rj);
});
}
/**
* Makes a request to get estimate of members affected by prune request.
*
* Promise resolves with an Object with following structure:
* ```js
* {
* guildId: String,
* days: Number, // same as parameter passed
* estimate: Number
* }
* ```
* @param {Number} days - Number of days from 1 to 7, default 1
* @returns {Promise<Object, Error>}
*/
getPruneEstimate(days) {
return rest(this._discordie).guilds.prune.getPrune(this.id, days);
}
/**
* Makes a request to prune members.
*
* Promise resolves with an Object with following structure:
* ```js
* {
* guildId: String,
* days: Number, // same as parameter passed
* pruned: Number
* }
* ```
* @param {Number} days - Number of days from 1 to 7, default 1
* @returns {Promise<Object, Error>}
*/
pruneMembers(days) {
// make it also accept object from .getPruneEstimate(days)
if (days && days.hasOwnProperty("days")) {
days = days.days;
}
return rest(this._discordie).guilds.prune.postPrune(this.id, days);
}
/**
* Makes a request to get a list of invites of this guild.
* @returns {Promise<Array<Object>, Error>}
*/
getInvites() {
return rest(this._discordie).guilds.getInvites(this.id);
}
/**
* Makes a request to get a list of voice regions available for this guild.
* @returns {Promise<Array<Object>, Error>}
*/
fetchRegions() {
return rest(this._discordie).voice.getRegions(this.id);
}
/**
* Makes a request to transfer ownership of this guild to `user`.
* @param {IGuildMember|IUser|String} user - User or an id string
* @returns {Promise<IGuild, Error>}
*/
transferOwnership(user) {
return new Promise((rs, rj) => {
rest(this._discordie)
.guilds.transferOwnership(this.id, user.valueOf())
.then(() => rs(this))
.catch(rj);
});
}
/**
* Makes a request to get widget (external embed) settings.
*
* Promise resolves with an Object with following structure:
* ```js
* {
* enabled: boolean,
* channel_id: String|null
* }
* ```
* @return {Promise<Object, Error>}
*/
getWidget() {
return rest(this._discordie).guilds.getEmbed(this.id);
}
/**
* Makes a request to set widget (external embed) settings.
* @param {Object} options
* Object `{enabled: boolean, channelId or channel_id: String}`.
* Both options are optional.
* @return {Promise<Object, Error>}
*/
editWidget(options) {
return rest(this._discordie).guilds.patchEmbed(this.id, options || {});
}
/**
* Makes a request to fetch emojis for this guild.
*
* Only user and whitelisted bot accounts can use this endpoint.
*
* Promise resolves with an array of Objects with following structure:
* ```js
* {
* "managed": false,
* "name": 'abc',
* "roles": [],
* "user": {
* "username": "testuser",
* "discriminator": "3273",
* "id": "000000000000000000",
* "avatar": null
* },
* "require_colons": true,
* "id": "000000000000000000"
* }
* ```
* @return {Promise<Array<Object>, Error>}
*/
fetchEmoji() {
return rest(this._discordie).guilds.emoji.getEmoji(this.id);
}
/**
* Makes a request to create an emoji.
*
* Returned object does not contain `user` property.
*
* Only user and whitelisted bot accounts can use this endpoint.
* @param {Buffer|String} image - Buffer or base64 image string
* @param {String} name
* @return {Promise<Object, Error>}
*/
uploadEmoji(image, name) {
if (image instanceof Buffer) {
image = Utils.imageToDataURL(image);
}
return rest(this._discordie).guilds.emoji.postEmoji(this.id, image, name);
}
/**
* Makes a request to delete the specified emoji.
*
* Only user and whitelisted bot accounts can use this endpoint.
* @param {Object|String} emoji - Emoji object or an id string
* @return {Promise<Object, Error>}
*/
deleteEmoji(emoji) {
if (emoji && emoji.id) emoji = emoji.id;
return rest(this._discordie).guilds.emoji.deleteEmoji(this.id, emoji);
}
/**
* Makes a request to edit the specified emoji.
*
* Returned object does not contain `user` property.
*
* Only user and whitelisted bot accounts can use this endpoint.
* @param {Object|String} emoji - Emoji object or an id string
* @param {Object} options
* Optional properties to edit:
* `{name: String, roles: Array<IRole>|Array<String>}`
* @return {Promise<Object, Error>}
*/
editEmoji(emoji, options) {
if (emoji && emoji.id) emoji = emoji.id;
if (!options) return Promise.resolve();
options = Object.assign({}, options);
const roles = options.roles;
if (roles && !Array.isArray(roles)) {
throw new TypeError("Param 'roles' must be an array");
}
if (Array.isArray(roles)) {
options.roles = roles.filter(role => role).map(role => role.valueOf());
}
return rest(this._discordie)
.guilds.emoji.patchEmoji(this.id, emoji, options);
}
/**
* Creates a string URL of an emoji.
* @returns {String|null}
*/
getEmojiURL(emoji) {
if (emoji && emoji.id) emoji = emoji.id;
if (!emoji) return null;
return Constants.CDN_ENDPOINT + Endpoints.CDN_EMOJI(emoji);
}
}
IGuild._inherit(Guild, function modelPropertyGetter(key) {
return this._discordie._guilds.get(this._guildId)[key];
});
/**
* Creates an array of roles of this guild.
* @readonly
* @instance
* @memberOf IGuild
* @name roles
* @returns {Array<IRole>}
*/
IGuild._setValueOverride("roles", function(rolesRaw) {
const roles = [];
if (!rolesRaw) return roles;
for (let role of rolesRaw.values()) {
roles.push(new IRole(this._discordie, role.id, this.id));
}
return roles;
});
module.exports = IGuild;

View File

@ -0,0 +1,78 @@
"use strict";
const ICollectionBase = require("./ICollectionBase");
const IGuild = require("./IGuild");
const Utils = require("../core/Utils");
const rest = require("../networking/rest");
/**
* @interface
* @extends ICollectionBase
*/
class IGuildCollection extends ICollectionBase {
constructor(discordie, valuesGetter, valueGetter) {
super({
valuesGetter: valuesGetter,
valueGetter: valueGetter,
itemFactory: (id) => new IGuild(this._discordie, id)
});
this._discordie = discordie;
Utils.privatify(this);
}
/**
* Makes a request to create a guild.
* @param {String} name
* @param {String} region
* @param {Buffer|null} [icon]
* @param {Array<IRole|Object>} [roles]
* @param {Array<IChannel|Object>} [channels]
* @param {Number} [verificationLevel]
* See Discordie.VerificationLevel
* @param {Number} [defaultMessageNotifications]
* See Discordie.UserNotificationSettings
* @returns {Promise<IGuild, Error>}
*/
create(name, region, icon,
roles, channels,
verificationLevel, defaultMessageNotifications) {
if (icon instanceof Buffer) {
icon = Utils.imageToDataURL(icon);
}
const toRaw = (data, param) => {
data = data || [];
if (!Array.isArray(data))
throw TypeError("Param '" + param + "' must be an array");
return data.map(v => {
return typeof v.getRaw === "function" ? v.getRaw() : null;
}).filter(v => v);
};
roles = toRaw(roles, "roles");
channels = toRaw(channels, "channels");
return new Promise((rs, rj) => {
rest(this._discordie).guilds.createGuild(
name, region, icon,
roles, channels,
verificationLevel, defaultMessageNotifications
)
.then(guild => rs(this._discordie.Guilds.get(guild.id)))
.catch(rj);
});
}
/**
* Makes a request to get a default list of voice regions.
* Use IGuild.fetchRegions for getting guild-specific list.
* @returns {Promise<Array<Object>, Error>}
*/
fetchRegions() {
return rest(this._discordie).voice.getRegions();
}
}
module.exports = IGuildCollection;

291
node_modules/discordie/lib/interfaces/IGuildMember.js generated vendored Normal file
View File

@ -0,0 +1,291 @@
"use strict";
const IBase = require("./IBase");
const IUser = require("./IUser");
const IRole = require("./IRole");
const GuildMember = require("../models/GuildMember");
const Utils = require("../core/Utils");
const rest = require("../networking/rest");
/**
* @interface
* @model GuildMember
* @extends IUser
*/
class IGuildMember extends IUser {
constructor(discordie, userId, guildId) {
super(discordie, userId);
Utils.definePrivate(this, {_guildId: guildId});
Object.freeze(this);
}
/**
* Current status of the member.
* @returns {String}
* @readonly
*/
get status() {
return this._discordie._presences.getStatus(this.id, this._guildId);
}
/**
* Current game the member is playing.
* @returns {Object|null}
* @readonly
*/
get game() {
return this._discordie._presences.getGame(this.id, this._guildId);
}
/**
* Name of the current game the member is playing.
* @returns {String|null}
* @readonly
*/
get gameName() {
return this.game ? this.game.name : null;
}
/**
* Previous status of the member.
* @returns {String}
* @readonly
*/
get previousStatus() {
return this._discordie._presences.getPreviousStatus(this.id, this._guildId);
}
/**
* Previous game the member was playing.
* @returns {Object|null}
* @readonly
*/
get previousGame() {
return this._discordie._presences.getPreviousGame(this.id, this._guildId);
}
/**
* Name of the previous game the member was playing.
* @returns {String|null}
* @readonly
*/
get previousGameName() {
return this.previousGame ? this.previousGame.name : null;
}
/**
* Gets guild of this member.
* @returns {IGuild|null}
* @readonly
*/
get guild() {
return this._discordie.Guilds.get(this._guildId);
}
/**
* Gets `nick` of this member if set, otherwise returns `username`.
* @returns {String|null}
* @readonly
*/
get name() {
return this.nick ? this.nick : this.username;
}
/**
* Gets the first voice channel that this member is currently in.
* @returns {IVoiceChannel|null}
*/
getVoiceChannel() {
return super.getVoiceChannel(this._guildId);
}
/**
* Makes a request to kick this member (from the guild they belong to).
* @returns {Promise}
*/
kick() {
return rest(this._discordie)
.guilds.members.kickMember(this._guildId, this.id);
}
/**
* Makes a request to ban this member (from the guild they belong to).
*
* Additionally delete `deleteMessageForDays` number of days worth of their
* messages from all channels of the guild.
* @param deleteMessageForDays - Number of days worth of messages to delete
* (min 0, max 7)
* @returns {Promise}
*/
ban(deleteMessageForDays) {
return rest(this._discordie)
.guilds.bans.banMember(this._guildId, this._userId, deleteMessageForDays);
}
/**
* Makes a request to unban this member (from the guild they belonged to).
* @returns {Promise}
*/
unban() {
return rest(this._discordie)
.guilds.bans.unbanMember(this._guildId, this._userId);
}
/**
* Makes a request to mute this member globally in the guild.
*
* Returns a resolved promise if the member is already muted.
* @returns {Promise}
*/
serverMute(mute) {
if (mute === undefined) mute = true;
if (mute == this.mute) return Promise.resolve();
return rest(this._discordie)
.guilds.members.setMute(this._guildId, this.id, mute);
}
/**
* Makes a request to unmute this member globally in the guild.
*
* Returns a resolved promise if the member is already unmuted.
* @returns {Promise}
*/
serverUnmute() {
return this.serverMute(false);
}
/**
* Makes a request to deafen this member globally in the guild.
*
* Returns a resolved promise if the member is already deafened.
* @returns {Promise}
*/
serverDeafen(deaf) {
if (deaf === undefined) deaf = true;
if (deaf == this.deaf) return Promise.resolve();
return rest(this._discordie)
.guilds.members.setDeaf(this._guildId, this.id, deaf);
}
/**
* Makes a request to undeafen this member globally in the guild.
*
* Returns a resolved promise if the member is already undeafened.
* @returns {Promise}
*/
serverUndeafen() {
return this.serverDeafen(false);
}
/**
* Checks if this member has the specified role.
* @param {IRole|String} role - Role or an id string
* @returns {Promise}
*/
hasRole(role) {
role = role.valueOf();
return this.getRaw().roles.indexOf(role) >= 0;
}
/**
* Assigns (adds) the specified role to this member.
* @param {IRole|String} role - Role or an id string
* @returns {Promise}
*/
assignRole(role) {
role = role.valueOf();
const rawMember = this.getRaw();
if (!rawMember || !rawMember.roles)
return Promise.reject(new Error("Member does not exist"));
// raw roles are mutable, copying with .slice()
const roleIds = rawMember.roles.slice();
const roleIndex = roleIds.indexOf(role);
if (roleIndex >= 0) return Promise.resolve();
roleIds.push(role);
return rest(this._discordie)
.guilds.members.setRoles(this._guildId, this.id, roleIds);
}
/**
* Unassigns (removes) the specified role from this member.
* @param {IRole|String} role - Role or an id string
* @returns {Promise}
*/
unassignRole(role) {
role = role.valueOf();
const rawMember = this.getRaw();
if (!rawMember || !rawMember.roles)
return Promise.reject(new Error("Member does not exist"));
// raw roles are mutable, copying with .slice()
const roleIds = rawMember.roles.slice();
const roleIndex = roleIds.indexOf(role);
if (roleIndex < 0) return Promise.resolve();
roleIds.splice(roleIndex, 1);
return rest(this._discordie)
.guilds.members.setRoles(this._guildId, this.id, roleIds);
}
/**
* Sets specified roles for this member: overwrites all existing roles
* with a new set of roles.
* @param {Array<IRole|String>} roles - Array of roles or id strings
* @returns {Promise}
*/
setRoles(roles) {
const roleIds = roles.map(role => role.valueOf());
return rest(this._discordie)
.guilds.members.setRoles(this._guildId, this.id, roleIds);
}
/**
* Moves this member to the specified voice channel.
* @param {IChannel|String} channel - Channel or an id string
* @returns {Promise}
*/
setChannel(channel) {
channel = channel.valueOf();
return rest(this._discordie)
.guilds.members.setChannel(this._guildId, this.id, channel);
}
/**
* Makes a request to set a nickname for this member.
*
* Requires permission `MANAGE_NICKNAMES`.
* @param {String} nick
* @returns {Promise}
*/
setNickname(nick) {
return rest(this._discordie)
.guilds.members.setNickname(this._guildId, this.id, nick);
}
}
IGuildMember._inherit(GuildMember, function modelPropertyGetter(key) {
return this._discordie._members.getMember(this._guildId, this._userId)[key];
});
/**
* Creates an array of roles assigned to this member.
* @readonly
* @instance
* @memberOf IGuildMember
* @name roles
* @returns {Array<IRole>}
*/
IGuildMember._setValueOverride("roles", function(roleIds) {
const roles = [];
if (!roleIds) return roles;
for (let roleId of roleIds) {
roles.push(new IRole(this._discordie, roleId, this._guildId));
}
return roles;
});
module.exports = IGuildMember;

101
node_modules/discordie/lib/interfaces/IInviteManager.js generated vendored Normal file
View File

@ -0,0 +1,101 @@
"use strict";
const Utils = require("../core/Utils");
const rest = require("../networking/rest");
/**
* @interface
*/
class IInviteManager {
constructor(discordie) {
this._discordie = discordie;
Utils.privatify(this);
Object.freeze(this);
}
/**
* Makes a request to create an invite.
* See `IChannel.createInvite` for more info.
* @see IChannel.createInvite
* @param {IChannel|String} channel
* @param {Object} options
* @returns {Promise<Object, Error>}
*/
create(channel, options) {
options = options || {
max_age: 60 * 30,
// value in seconds
max_uses: 0,
// pretty obvious
temporary: false
// temporary membership, kicks members without roles on disconnect
};
channel = channel.valueOf();
return rest(this._discordie).invites.createInvite(channel, options);
}
/**
* Makes a request to regenerate existing invite.
* @param {Object|String} code
* @returns {Promise<Object, Error>}
*/
regenerate(code) {
if (code && code.code) code = code.code;
const options = {regenerate: code};
return rest(this._discordie).invites.createInvite(channel, options);
}
/**
* Makes a request to revoke existing invite.
* @param {Object|String} code
* @returns {Promise<Object, Error>}
*/
revoke(code) {
if (code && code.code) code = code.code;
return rest(this._discordie).invites.deleteInvite(code);
}
/**
* Makes a request to resolve existing invite.
* @param {Object|String} code
* @returns {Promise<Object, Error>}
* @example
* client.Invites.resolve("Zt5yW").then(console.log).catch(console.log);
* // Example response:
* {
* "code": "Zt5yW",
* "guild": {
* "splash_hash": null,
* "id": "00000000000000000",
* "name": "test"
* },
* "channel": {
* "type": "text",
* "id": "000000000000000000",
* "name": "testchannel"
* }
* }
*/
resolve(code) {
if (code && code.code) code = code.code;
return rest(this._discordie).invites.getInvite(code);
}
/**
* **Deprecated**: Only works with user accounts.
* Bot accounts can be invited by users with Manage Server permission using
* the `https://discordapp.com/oauth2/authorize?client_id=%APP_ID%&scope=bot`
* page. See official Discord API documentation for more info.
*
* Makes a request to accept existing invite.
* @param {Object|String} code
* @returns {Promise<Object, Error>}
*/
accept(code) {
if (code && code.code) code = code.code;
return rest(this._discordie).invites.postInvite(code);
}
}
module.exports = IInviteManager;

430
node_modules/discordie/lib/interfaces/IMessage.js generated vendored Normal file
View File

@ -0,0 +1,430 @@
"use strict";
const Constants = require("../Constants");
const MessageTypes = Constants.MessageTypes;
const IBase = require("./IBase");
const IRole = require("./IRole");
const IUser = require("./IUser");
const Utils = require("../core/Utils");
const Message = require("../models/Message");
const rest = require("../networking/rest");
/**
* @interface
* @model Message
* @extends IBase
* @description
* Chat message. Can be a system message depending on type:
*
* ```js
* Discordie.MessageTypes: {
* DEFAULT: 0,
* RECIPIENT_ADD: 1,
* RECIPIENT_REMOVE: 2,
* CALL: 3,
* CHANNEL_NAME_CHANGE: 4,
* CHANNEL_ICON_CHANGE: 5,
* CHANNEL_PINNED_MESSAGE: 6
* }
* ```
*/
class IMessage extends IBase {
constructor(discordie, messageId) {
super();
Utils.definePrivate(this, {
_discordie: discordie,
_messageId: messageId
});
Object.freeze(this);
}
/**
* Checks whether this message is cached.
* @param {IMessage} message
* @returns {boolean}
* @readonly
*/
get isCached() {
return !!this._valid;
}
/**
* Checks whether this message was edited by the author.
*
* Returns null if message does not exist in cache.
* @returns {boolean|null}
* @readonly
*/
get isEdited() {
return this.isCached ? this.edited_timestamp != null : null;
}
/**
* Checks whether this message is from a private channel (direct message).
*
* Returns null if message/channel does not exist in cache.
* @returns {boolean|null}
* @readonly
*/
get isPrivate() {
return this._discordie._channels.isPrivate(this.channel_id);
}
/**
* Checks whether the message is a system message.
* @returns {boolean|null}
* @readonly
*/
get isSystem() {
return this.type !== MessageTypes.DEFAULT;
}
/**
* Generates a system message string depending on message `type`.
* @returns {String|null}
* @readonly
*/
get systemMessage() {
if (!this._valid) return null;
const type = this.type;
const user = this.author || {};
const target = this.mentions[0] || {};
if (type === MessageTypes.RECIPIENT_ADD) {
return user.username + " added " + target.username + " to the group.";
}
if (type === MessageTypes.RECIPIENT_REMOVE) {
if (user.id !== target.id) {
return user.username + " added " + target.username + " to the group.";
} else {
return user.username + " left the group.";
}
}
if (type === MessageTypes.CALL) {
const localUserId = this._discordie._user && this._discordie._user.id;
const isActive =
this._discordie._calls.isActive(this.channel_id, this.id);
const isMissed =
!isActive &&
this.call && this.call.participants &&
this.call.participants.indexOf(localUserId) < 0;
if (isMissed) {
return "You missed a call from " + user.username + ".";
}
if (isActive) {
return user.username + " started a call. Join the call.";
} else {
return user.username + " started a call.";
}
}
if (type === MessageTypes.CHANNEL_NAME_CHANGE) {
return user.username + " changed the channel name: " + this.content;
}
if (type === MessageTypes.CHANNEL_ICON_CHANGE) {
return user.username + " changed the channel icon.";
}
if (type === MessageTypes.CHANNEL_PINNED_MESSAGE) {
return user.username +
" pinned a message to this channel. See all the pins.";
}
}
/**
* Resolves username that should be displayed with this message.
* @return {String|null}
* @readonly
*/
get displayUsername() {
if (!this._valid) return null;
const member = this.member;
const author = this.author;
const nick = member ? member.nick : null;
const username = author ? author.username : null;
return nick || username;
}
/**
* Gets channel of this message.
*
* Returns null if message does not exist in cache.
* @returns {ITextChannel|IDirectMessageChannel|null}
* @readonly
*/
get channel() {
if (this.isPrivate) {
return this._discordie.DirectMessageChannels.get(this.channel_id);
}
return this._discordie.Channels.get(this.channel_id);
}
/**
* Gets guild of this message.
*
* Returns null if message does not exist in cache or from a private
* channel (direct message).
* @returns {IGuild|null}
* @readonly
*/
get guild() {
if (this.isPrivate || !this.isCached) return null;
return this.channel ? this.channel.guild : null;
}
/**
* Gets member instance of author.
*
* Returns null for private channels, if message does not exist in cache,
* the author is no longer a member of the guild, or it is a webhook message.
* @returns {IGuildMember|null}
* @readonly
*/
get member() {
if (this.isPrivate || !this.isCached) return null;
return this._discordie.Users.getMember(this.guild.id, this.author.id);
}
/**
* Creates an array of all known (cached) versions of this message (including
* the latest).
* Sorted from latest (first) to oldest (last).
* Does not include embeds.
* @returns {Array<Object>}
* @readonly
*/
get edits() {
return this._discordie._messages.getEdits(this.id);
}
/**
* Resolves user and channel references in `content` property to proper names.
* References that are not found in cache will be left as is and not resolved.
*
* Returns null if this message is not cached.
* @returns {String|null}
* @example
* var content = message.content;
* // 'just a message for <@157838423632817262> in <#78826383786329613>'
* var resolvedContent = message.resolveContent();
* // 'just a message for @testie in #general'
* var resolvedContent = client.Messages.resolveContent(message.content);
* // 'just a message for @testie in #general'
*/
resolveContent() {
if (!this.isCached) return null;
return this._discordie.Messages.resolveContent(this.content, this.guild);
}
/**
* Makes a request to edit this message.
*
* Editing of other users' messages is not allowed, server will send an
* `Error` `Forbidden` and returned promise will be rejected if you attempt
* to do so.
*
* See `IMessageCollection.editMessage` if you are looking for a method
* that can operate on JSON or raw message id.
* @param {String} [content]
* @param {Object} [embed]
* Refer to [official API documentation](https://discordapp.com/developers/docs/resources/channel#embed-object)
* for embed structure description.
* @returns {Promise<IMessage, Error>}
*/
edit(content, embed) {
if (Array.isArray(content)) content = content.join("\n");
if (content && typeof content !== "string") content = String(content);
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.patchMessage(this.channel_id, this.id, content, embed)
.then(() => rs(this))
.catch(rj);
});
}
/**
* Makes a request to delete this message.
*
* See `IMessageCollection.deleteMessage` if you are looking for a method
* that can operate on JSON or raw message id.
* @returns {Promise}
*/
delete() {
return rest(this._discordie)
.channels.deleteMessage(this.channel_id, this.id);
}
/**
* Makes a request to pin this message.
*
* See `IMessageCollection.pinMessage` if you are looking for a method
* that can operate on JSON or raw message id.
* @returns {Promise<IMessage, Error>}
*/
pin() {
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.pinMessage(this.channel_id, this.id)
.then(() => rs(this))
.catch(rj);
});
}
/**
* Makes a request to unpin this message.
*
* See `IMessageCollection.unpinMessage` if you are looking for a method
* that can operate on JSON or raw message id.
* @returns {Promise<IMessage, Error>}
*/
unpin() {
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.unpinMessage(this.channel_id, this.id)
.then(() => rs(this))
.catch(rj);
});
}
/**
* Makes a request to fetch users who reacted to this message with the
* specified emoji.
* @param {Object|String} emoji
* Partial emoji `{id: String|null, name: String}` or a unicode emoji
* @param {Number} [limit] - Max 100 users per fetch
* @param {IUser|String} [after] - Start list from specified user id
* @return {Promise<Array<IUser>, Error>}
*/
fetchReactions(emoji, limit, after) {
emoji = Utils.emojiToCode(emoji);
limit = limit || 100;
after = after ? after.valueOf() : after;
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.getReactions(this.channel_id, this.id, emoji, limit, after)
.then(users => rs(users.map(user => client.Users.get(user.id))))
.catch(rj);
});
}
/**
* Makes a request to add a reaction to this message with the specified
* emoji.
* @param {Object|String} emoji
* Partial emoji `{id: String|null, name: String}` or a unicode emoji
* @return {Promise}
* @example
* message.addReaction(message.reactions[0].emoji);
* message.addReaction("\uD83D\uDE2C");
*/
addReaction(emoji) {
emoji = Utils.emojiToCode(emoji);
return rest(this._discordie)
.channels.addReaction(this.channel_id, this.id, emoji);
}
/**
* Makes a request to remove a reaction to this message with the specified
* emoji.
* @param {Object|String} emoji
* Partial emoji `{id: String|null, name: String}` or a unicode emoji
* @param {IUser|String} [user] - Remove reaction of another user
* @return {Promise}
*/
removeReaction(emoji, user) {
emoji = Utils.emojiToCode(emoji);
user = user ? user.valueOf() : user;
return rest(this._discordie)
.channels.removeReaction(this.channel_id, this.id, emoji, user);
}
/**
* Makes a request to remove all reactions from this message.
* @return {Promise}
*/
clearReactions() {
return rest(this._discordie)
.channels.deleteReactions(this.channel_id, this.id);
}
/**
* Makes a request to send a reply to channel the message was from, prefixing
* content with author's mention in non-private channels.
* @param {String|Array<String>} content
* Strings will be sent as is, arrays - joined with a newline character.
* @param {IUser|IGuildMember|Array<IUser>|Array<IGuildMember>} [mentions]
* Deprecated: left for backward compatibility.
* @param {boolean} [tts]
* @param {Object} [embed]
* Refer to [official API documentation](https://discordapp.com/developers/docs/resources/channel#embed-object)
* for embed structure description.
* @returns {Promise<IMessage, Error>}
*/
reply(content, mentions, tts, embed) {
if (this.isPrivate) {
return this.channel.sendMessage(content, mentions, tts, embed);
}
return this.channel
.sendMessage(`${this.author.mention}, ${content}`, mentions, tts, embed);
}
}
IMessage._inherit(Message, function modelPropertyGetter(key) {
return this._discordie._messages.get(this._messageId)[key];
});
IMessage._setSuppressErrors(true);
/**
* @readonly
* @instance
* @memberOf IMessage
* @name author
* @returns {IUser}
*/
IMessage._setValueOverride("author", function(user) {
if (user && this.webhook_id) {
const factory = () => new IUser(this._discordie, user.id, user);
return this._discordie.Users._getOrCreateInterface(user, factory);
}
return user ? this._discordie.Users.get(user.id) : null;
});
/**
* @readonly
* @instance
* @memberOf IMessage
* @name mentions
* @returns {Array<IUser>}
*/
IMessage._setValueOverride("mentions", function(v) {
return v ? v.map(u => this._discordie.Users.get(u.id)) : [];
});
/**
* @readonly
* @instance
* @memberOf IMessage
* @name mention_roles
* @returns {Array<IRole>}
*/
IMessage._setValueOverride("mention_roles", function(rolesRaw) {
if (!rolesRaw || !this.guild) return [];
var guildId = this.guild.id;
return rolesRaw.map(id => new IRole(this._discordie, id, guildId));
});
module.exports = IMessage;

View File

@ -0,0 +1,374 @@
"use strict";
const ICollectionBase = require("./ICollectionBase");
const IMessage = require("./IMessage");
const IChannel = require("./IChannel");
const Utils = require("../core/Utils");
const rest = require("../networking/rest");
/**
* @interface
* @extends ICollectionBase
* @description
* Collection with all cached messages.
*
* **Includes deleted messages** - you can filter them by checking
* `IMessage.deleted` boolean.
*/
class IMessageCollection extends ICollectionBase {
constructor(discordie, valuesGetter, valueGetter) {
super({
valuesGetter: valuesGetter,
valueGetter: valueGetter,
itemFactory: (id) => new IMessage(this._discordie, id)
});
this._discordie = discordie;
Utils.privatify(this);
}
/**
* Creates an array of cached messages in `channel`, sorted in order of
* arrival (message cache is sorted on message insertion, not when this
* getter is invoked).
*
* Returns an empty array if channel no longer exists.
*
* > **Note:** Message cache also includes deleted messages.
* > You can filter them by checking `IMessage.deleted` boolean.
* @param {IChannel|String} channel
* @returns {Array<IMessage>}
*/
forChannel(channel) {
var cache = this._discordie._messages.getChannelCache(channel.valueOf());
if (!cache || !cache.size) return [];
return cache.map(message => this._getOrCreateInterface(message));
}
/**
* Purges channel cache.
* @param {IChannel|String} channel
*/
purgeChannelCache(channel) {
channel = channel.valueOf();
return this._discordie._messages.purgeChannelCache(channel);
}
/**
* Creates an array of cached pinned messages in `channel`.
*
* Pinned message cache is updated only if all pinned messages have been
* loaded with `ITextChannel.fetchPinned()`.
*
* Returns an empty array if channel no longer exists or if pinned messages
* have not been fetched yet.
* @param {IChannel|String} channel
* @returns {Array<IMessage>}
*/
forChannelPinned(channel) {
var cache = this._discordie._messages.getChannelPinned(channel.valueOf());
if (!cache) return [];
return cache.map(msg => this._getOrCreateInterface(msg));
}
/**
* Purges pinned message cache for `channel`.
* @param {IChannel|String} channel
*/
purgeChannelPinned(channel) {
channel = channel.valueOf();
return this._discordie._messages.purgeChannelPinned(channel);
}
/**
* Purges pinned message cache globally.
*/
purgePinned() {
return this._discordie._messages.purgePinned();
}
/**
* Purges edits cache globally.
*/
purgeEdits() {
return this._discordie._messages.purgeEdits();
}
/**
* Purges message cache globally.
*/
purgeAllCache() {
return this._discordie._messages.purgeAllCache();
}
/**
* Gets channel message cache limit.
* @param {IChannel|String} channel
* @returns {Number}
*/
getChannelMessageLimit(channel) {
channel = channel.valueOf();
return this._discordie._messages.getChannelMessageLimit(channel);
}
/**
* Sets channel message cache limit (with a minimum of 1).
* Limit reverts to default when channel (or cache) is destroyed.
* Returns false if limit is invalid or channel does not exist.
* @param {IChannel|String} channel
* @param {Number} limit
* @returns {Boolean}
*/
setChannelMessageLimit(channel, limit) {
channel = channel.valueOf();
return this._discordie._messages.setChannelMessageLimit(channel, limit);
}
/**
* Gets global message cache limit per channel.
* @returns {Number}
*/
getMessageLimit() {
return this._discordie._messages.getMessageLimit();
}
/**
* Sets global message cache limit per channel (with a minimum of 1).
* Does not affect channels with custom limits if new is lower than current.
* @param {Number} limit
*/
setMessageLimit(limit) {
return this._discordie._messages.setMessageLimit(limit);
}
/**
* Gets global edits cache limit per message.
* @returns {Number}
*/
getEditsLimit() {
return this._discordie._messages.getEditsLimit();
}
/**
* Sets global edits cache limit per message.
* @param {Number} limit
*/
setEditsLimit(limit) {
return this._discordie._messages.setEditsLimit(limit);
}
/**
* Makes a request to edit a message.
* Alternative method for editing messages that are not in cache.
*
* Editing of other users' messages is not allowed, server will send an
* `Error` `Forbidden` and returned promise will be rejected if you attempt
* to do so.
*
* Parameter `messageId` can be an object with fields `{channel_id, id}`,
* where `id` is a String message id, `channel_id` is a String channel id.
*
* Parameter `channelId` is ignored when `messageId` is an object or
* an instance of `IMessage`.
*
* Returns a promise that resolves to a JSON object of the edited message.
*
* @param {String|Object} [content]
* @param {IMessage|Object|String} messageId
* @param {String} channelId - Ignored if `messageId` is an object with `id`
* @param {Object} [embed]
* Refer to [official API documentation](https://discordapp.com/developers/docs/resources/channel#embed-object)
* for embed structure description.
* @returns {Promise<Object, Error>}
* @example
* var message = client.Messages.find(m => true); // get any message
* client.Messages.editMessage("new content", message);
*
* var jsonMessage = {id: message.id, channel_id: message.channel_id};
* client.Messages.editMessage("new content", jsonMessage);
*
* client.Messages.editMessage("new content", message.id, message.channel_id);
*/
editMessage(content, messageId, channelId, embed) {
if (Array.isArray(content)) content = content.join("\n");
if (content && typeof content !== "string") content = String(content);
var message = {id: messageId, channel_id: channelId};
if (messageId && messageId.id) {
message.id = messageId.id;
message.channel_id = messageId.channel_id || messageId.channelId;
}
return rest(this._discordie)
.channels.patchMessage(message.channel_id, message.id, content, embed);
}
/**
* Makes a request to delete a message.
* Alternative method for deleting messages that are not in cache.
*
* Parameter `messageId` can be an object with fields `{channel_id, id}`,
* where `id` is a String message id, `channel_id` is a String channel id.
*
* Parameter `channelId` is ignored when `messageId` is an object or
* an instance of `IMessage`.
*
* @param {IMessage|Object|String} messageId
* @param {String} channelId - Ignored if `messageId` is an object with `id`
* @returns {Promise}
* @example
* var message = client.Messages.find(m => true); // get any message
* client.Messages.deleteMessage(message);
*
* var jsonMessage = {id: message.id, channel_id: message.channel_id};
* client.Messages.deleteMessage(jsonMessage);
*
* client.Messages.deleteMessage(message.id, message.channel_id);
*/
deleteMessage(messageId, channelId) {
var message = {id: messageId, channel_id: channelId};
if (messageId && messageId.id) {
message.id = messageId.id;
message.channel_id = messageId.channel_id || messageId.channelId;
}
return rest(this._discordie)
.channels.deleteMessage(message.channel_id, message.id);
}
/**
* Makes a request to pin a message.
* Alternative method for pinning messages that are not in cache.
*
* Accepts same parameters as `IMessageCollection.deleteMessage`.
*
* @param {IMessage|Object|String} messageId
* @param {String} channelId - Ignored if `messageId` is an object with `id`
* @returns {Promise}
*/
pinMessage(messageId, channelId) {
var message = {id: messageId, channel_id: channelId};
if (messageId && messageId.id) {
message.id = messageId.id;
message.channel_id = messageId.channel_id || messageId.channelId;
}
return rest(this._discordie)
.channels.pinMessage(message.channel_id, message.id);
}
/**
* Makes a request to unpin a message.
* Alternative method for unpinning messages that are not in cache.
*
* Accepts same parameters as `IMessageCollection.deleteMessage`.
*
* @param {IMessage|Object|String} messageId
* @param {String} channelId - Ignored if `messageId` is an object with `id`
* @returns {Promise}
*/
unpinMessage(messageId, channelId) {
var message = {id: messageId, channel_id: channelId};
if (messageId && messageId.id) {
message.id = messageId.id;
message.channel_id = messageId.channel_id || messageId.channelId;
}
return rest(this._discordie)
.channels.unpinMessage(message.channel_id, message.id);
}
/**
* Makes a request to delete multiple messages.
*
* If `messages` array contains instances of `IMessage`, parameter `channel`
* is not required as it will be determined from the first message instance.
* Also deleted messages will be omitted from the request.
*
* If `messages` array is empty, returned promise resolves instantly
* without sending a request.
* @param {Array<IMessage|String>} messages
* @param {IChannel|String} [channel]
* Channel or channel id, required is `messages` is an array of string ids
* @returns {Promise}
*/
deleteMessages(messages, channel) {
if (!Array.isArray(messages))
throw new TypeError("Param 'messages' must be an array");
messages = messages.filter(m => {
if (m instanceof IMessage)
return !m.deleted;
return true;
});
if (!messages.length) return Promise.resolve();
var internalMessage = messages.find(v => v.channel_id);
if (!internalMessage && !channel)
throw new TypeError("Param 'channel' must be defined for arrays of ids");
channel = (internalMessage && !channel) ?
internalMessage.channel_id :
channel.valueOf();
// bulk-delete returns 'Bad Request' in this case
if (messages.length === 1) {
return rest(this._discordie)
.channels.deleteMessage(channel, messages[0].valueOf());
}
return rest(this._discordie)
.channels.deleteMessages(channel, messages.map(v => v.valueOf()));
}
/**
* Resolves user and channel references to proper names.
* References that are not found in cache will be left as is and not resolved.
* @param {String} content
* @param {IGuild|String} [guild]
* Optional guild to resolve roles and nicknames from
* @returns {String}
* @example
* var content = message.content;
* // 'just a message for <@157838423632817262> in <#78826383786329613>'
* var resolvedContent = message.resolveContent();
* // 'just a message for @testie in #general'
* var resolvedContent = client.Messages.resolveContent(message.content);
* // 'just a message for @testie in #general'
*/
resolveContent(content, guild) {
if (typeof content !== "string")
throw new TypeError("Param 'content' is not a string");
if (guild) {
var guildId = guild.valueOf();
guild = this._discordie.Guilds.get(guildId);
}
return content.replace(/<(@!?|#|@&)([0-9]+)>/g, (match, type, id) => {
if (type === "@" || type === "@!") { // user
var user = this._discordie.Users.get(id);
if (!user) return match;
if (guild && type === "@!") {
var member = user.memberOf(guild);
return (member && ("@" + member.name)) || match;
}
return (user && ("@" + user.username)) || match;
}
else if (type === "#") { // channel
var channel = this._discordie.Channels.get(id);
return (channel && ("#" + channel.name)) || match;
}
else if (type === "@&") { // role
if (!guild || !guild.roles) return match;
var role = guild.roles.find(r => r.id === id);
return (role && ("@" + role.name)) || match;
}
});
}
}
module.exports = IMessageCollection;

View File

@ -0,0 +1,121 @@
"use strict";
const IBase = require("./IBase");
const IPermissions = require("./IPermissions");
const Utils = require("../core/Utils");
const PermissionOverwrite = require("../models/PermissionOverwrite");
const Constants = require("../Constants");
const ChannelTypes = Constants.ChannelTypes;
const PermissionSpecs = Constants.PermissionSpecs;
const rest = require("../networking/rest");
/**
* @interface
* @model PermissionOverwrite
* @extends IBase
*/
class IPermissionOverwrite extends IBase {
constructor(discordie, overwriteId, channelId) {
super();
this._discordie = discordie;
this._overwriteId = overwriteId;
this._channelId = channelId;
const channel = discordie._channels.get(channelId) || {};
const spec = (channel.type == ChannelTypes.GUILD_VOICE ?
PermissionSpecs.VoiceChannel :
PermissionSpecs.TextChannel
);
this._allow = new IPermissions(this.getRaw().allow, spec);
this._deny = new IPermissions(this.getRaw().deny, spec);
Utils.privatify(this);
Object.freeze(this);
}
/**
* Gets date and time the member or role of this overwrite was created at.
* @returns {Date}
* @readonly
* @ignore
*/
get createdAt() {
return new Date(Utils.timestampFromSnowflake(this.id));
}
/**
* Loads original permissions from cache and updates this object.
*/
reload() {
const raw = this.getRaw();
if (!raw) return;
this._allow.raw = raw.allow;
this._deny.raw = raw.deny;
}
/**
* Makes a request to commit changes made to this permission overwrite object.
* @returns {Promise<IPermissionOverwrite, Error>}
*/
commit() {
return new Promise((rs, rj) => {
const raw = this.getRaw();
raw.allow = this._allow.raw;
raw.deny = this._deny.raw;
rest(this._discordie)
.channels.putPermissionOverwrite(this._channelId, raw)
.then(() => rs(this))
.catch(e => {
this.reload();
return rj(e);
});
});
}
/**
* Makes a request to delete this permission overwrite.
* @returns {Promise}
*/
delete() {
return rest(this._discordie)
.channels.deletePermissionOverwrite(this._channelId, this.id);
}
}
IPermissionOverwrite._inherit(PermissionOverwrite, function(key) {
const channel = this._discordie._channels.get(this._channelId);
if (!channel) return null;
const overwrite = channel.permission_overwrites
.find(o => o.id == this._overwriteId);
if (!overwrite) return null;
return overwrite[key];
});
/**
* @readonly
* @instance
* @memberOf IPermissionOverwrite
* @name allow
* @returns {IPermissions}
*/
IPermissionOverwrite._setValueOverride("allow", function(v) {
return this._allow;
});
/**
* @readonly
* @instance
* @memberOf IPermissionOverwrite
* @name deny
* @returns {IPermissions}
*/
IPermissionOverwrite._setValueOverride("deny", function(v) {
return this._deny;
});
module.exports = IPermissionOverwrite;

202
node_modules/discordie/lib/interfaces/IPermissions.js generated vendored Normal file
View File

@ -0,0 +1,202 @@
"use strict";
const Constants = require("../Constants");
const Permissions = Constants.Permissions;
const PermissionsDefault = Constants.PermissionsDefault;
/**
* @interface
* @description
* Wrapper for numeric permission values.
*
* Contains boolean getters/setters (different for roles and channels).
*
* - `.Text` section only exists for text channels;
* - `.Voice` section only exists for voice channels;
* - `.General` section exists for both;
* - Roles contain all properties - both `.Text` and `.Voice` sections.
*
* Example of role permission properties:
* ```
* General: {
* CREATE_INSTANT_INVITE,
* KICK_MEMBERS,
* BAN_MEMBERS,
* ADMINISTRATOR,
* MANAGE_CHANNELS,
* MANAGE_GUILD,
* CHANGE_NICKNAME,
* MANAGE_NICKNAMES,
* MANAGE_ROLES,
* MANAGE_WEBHOOKS,
* MANAGE_EMOJIS,
* },
* Text: {
* READ_MESSAGES,
* SEND_MESSAGES,
* SEND_TTS_MESSAGES,
* MANAGE_MESSAGES,
* EMBED_LINKS,
* ATTACH_FILES,
* READ_MESSAGE_HISTORY,
* MENTION_EVERYONE,
* EXTERNAL_EMOTES,
* ADD_REACTIONS,
* },
* Voice: {
* CONNECT,
* SPEAK,
* MUTE_MEMBERS,
* DEAFEN_MEMBERS,
* MOVE_MEMBERS,
* USE_VAD,
* }
* ```
*
* Example of text channel permission properties:
* ```
* General: {
* CREATE_INSTANT_INVITE,
* MANAGE_CHANNEL,
* MANAGE_PERMISSIONS
* },
* Text: {
* READ_MESSAGES,
* SEND_MESSAGES,
* SEND_TTS_MESSAGES,
* MANAGE_MESSAGES,
* EMBED_LINKS,
* ATTACH_FILES,
* READ_MESSAGE_HISTORY,
* MENTION_EVERYONE,
* EXTERNAL_EMOTES,
* ADD_REACTIONS,
* }
* ```
*
* @example
* var guild = client.Guilds.find(g => g.name == "test");
* guild.createRole().then(role => {
* var perms = role.permissions;
* perms.General.KICK_MEMBERS = true;
* perms.General.BAN_MEMBERS = true;
* perms.Text.MENTION_EVERYONE = true;
*
* var newRoleName = "Testing";
* var color = 0xE74C3C; // red
* var hoist = true; // display as separate group
*
* role.commit(newRoleName, color, hoist);
* }).catch(err => console.log("Failed to create role:", err));
*/
class IPermissions {
constructor(raw, permissionSpec) {
this.raw = raw || 0;
for (let type in permissionSpec) {
this[type] = {};
for (let permission in permissionSpec[type]) {
const bit = permissionSpec[type][permission];
Object.defineProperty(this[type], permission, {
enumerable: true,
get: () => (this.raw & bit) === bit,
set: (v) => v ? (this.raw |= bit) : (this.raw &= ~bit)
});
}
Object.seal(this[type]);
}
Object.seal(this);
}
inspect() { return JSON.parse(JSON.stringify(this)); }
setAll() { this.raw = IPermissions.ALL; }
unsetAll() { this.raw = IPermissions.NONE; }
static get ALL() { return (~0 >>> 0); }
static get DEFAULT() { return PermissionsDefault; }
static get NONE() { return 0; }
static resolveRaw(user, context) {
// referencing here to avoid circular require()
const IUser = require("./IUser");
const IAuthenticatedUser = require("./IAuthenticatedUser");
const IChannel = require("./IChannel");
const IGuild = require("./IGuild");
const IGuildMember = require("./IGuildMember");
if (!(user instanceof IUser) && !(user instanceof IAuthenticatedUser))
throw new TypeError("user must be an instance of IUser");
if (!(context instanceof IChannel) && !(context instanceof IGuild))
throw new TypeError("context must be an instance of IChannel or IGuild");
if (!context._valid) throw new Error("Invalid context");
let overwrites = null;
if (context instanceof IChannel) {
overwrites = context.getRaw().permission_overwrites;
context = context.guild;
}
if (context.isOwner(user))
return IPermissions.ALL;
const member = user instanceof IGuildMember ?
user : context._discordie.Users.getMember(context.id, user.id);
if (!member) throw new Error("User is not a member of the context");
const contextRaw = context.getRaw();
const roleEveryone = contextRaw ? contextRaw.roles.get(context.id) : null;
// apply default permissions
let permissions = roleEveryone ?
roleEveryone.permissions : IPermissions.DEFAULT;
// then roles assigned for member
const memberRoles = member ? member.roles : null;
if (memberRoles) {
permissions = memberRoles.reduce(
(ps, role) => ps | role.permissions.raw,
permissions
);
}
if (permissions & Permissions.General.ADMINISTRATOR)
return IPermissions.ALL;
if (overwrites) {
const applyOverwrite = (overwrite) => {
if (!overwrite) return;
permissions &= ~overwrite.deny;
permissions |= overwrite.allow;
};
// then channel specific @everyone role
const overwriteEveryone = overwrites.find(o => o.id == context.id);
applyOverwrite(overwriteEveryone);
if (member) {
// then member roles for channel
if (memberRoles)
memberRoles.forEach(role =>
applyOverwrite(overwrites.find(o => o.id == role.id))
);
// then member specific permissions for channel
const overwriteMember = overwrites.find(o => o.id == member.id);
applyOverwrite(overwriteMember);
}
}
return permissions;
}
static resolve(user, context) {
return new IPermissions(
IPermissions.resolveRaw(user, context),
Permissions
);
}
}
module.exports = IPermissions;

132
node_modules/discordie/lib/interfaces/IRole.js generated vendored Normal file
View File

@ -0,0 +1,132 @@
"use strict";
const IBase = require("./IBase");
const IPermissions = require("./IPermissions");
const Utils = require("../core/Utils");
const Role = require("../models/Role");
const Constants = require("../Constants");
const PermissionSpecs = Constants.PermissionSpecs;
const rest = require("../networking/rest");
/**
* @interface
* @extends IBase
* @model Role
*/
class IRole extends IBase {
constructor(discordie, roleId, guildId) {
super();
Utils.definePrivate(this, {
_discordie: discordie,
_roleId: roleId,
_guildId: guildId
});
Utils.definePrivate(this, {
_permissions: new IPermissions(
this.getRaw("permissions"),
PermissionSpecs.Role
)
});
Object.freeze(this);
}
/**
* Creates a mention from this role's id.
* @returns {String}
* @readonly
* @example
* channel.sendMessage(role.mention + ", example mention");
*/
get mention() {
return `<@&${this.id}>`;
}
/**
* Loads original permissions from cache and updates this object.
*/
reload() {
const rawPermissions = this.getRaw("permissions");
if (!rawPermissions) return;
this._permissions.raw = rawPermissions;
}
/**
* Makes a request to commit changes made to this role object.
* @param {String} [name]
* @param {Number} [color] - RGB color number
* @param {boolean} [hoist] - true if role should be displayed separately
* @param {boolean} [mentionable]
* @returns {Promise}
*/
commit(name, color, hoist, mentionable) {
const everyone = (this.id == this._guildId);
if (!name || everyone) name = this.name;
if (hoist === undefined || hoist === null || everyone) hoist = this.hoist;
if (color === undefined || color === null || everyone) color = this.color;
if (mentionable === undefined || mentionable === null || everyone) {
mentionable = this.mentionable;
}
return new Promise((rs, rj) => {
rest(this._discordie).guilds.roles.patchRole(
this._guildId, this.id,
name, this.permissions.raw, color, hoist, mentionable
)
.then(() => rs(this))
.catch(err => {
this.reload();
return rj(err);
});
});
}
/**
* Moves this role to `position` and makes a batch role update request.
* @param {Number} position
* @returns {Promise}
*/
setPosition(position) {
const guild = this._discordie.Guilds.get(this._guildId);
if (!guild) return Promise.reject(new Error("Guild does not exist"));
// maybe todo: disallow assigning position 0 (role @everyone)
const changes = Utils.reorderObjects(guild.roles, this, position);
if (!changes) return Promise.resolve();
return rest(this._discordie)
.guilds.roles.batchPatchRoles(this._guildId, changes);
}
/**
* Makes a request to delete this role.
* @returns {Promise}
*/
delete() {
return rest(this._discordie)
.guilds.roles.deleteRole(this._guildId, this.id);
}
}
IRole._inherit(Role, function modelPropertyGetter(key) {
const guild = this._discordie._guilds.get(this._guildId);
if (!guild) return null;
const role = guild.roles.get(this._roleId);
if (!role) return null;
return role[key];
});
/**
* @readonly
* @instance
* @memberOf IRole
* @name permissions
* @returns {IPermissions}
*/
IRole._setValueOverride("permissions", function(v) {
return this._permissions;
});
module.exports = IRole;

283
node_modules/discordie/lib/interfaces/ITextChannel.js generated vendored Normal file
View File

@ -0,0 +1,283 @@
"use strict";
const Utils = require("../core/Utils");
const User = require("../models/User");
const IChannel = require("./IChannel");
const IUser = require("./IUser");
const Constants = require("../Constants");
const Permissions = Constants.Permissions;
const rest = require("../networking/rest");
/**
* @interface
* @model Channel
* @extends IChannel
*/
class ITextChannel extends IChannel {
constructor(discordie, channelId) {
super(discordie, channelId);
}
/**
* Creates a mention from this channel's id.
* @returns {String}
* @readonly
* @example
* channel.sendMessage(channel.mention + ", example mention");
*/
get mention() {
return `<#${this.id}>`;
}
/**
* Creates an array of IGuildMember that
* have permissions to read this channel.
* @returns {Array<IGuildMember>}
* @readonly
*/
get members() {
return this._discordie.Users.membersForChannel(this);
}
/**
* Gets a value indicating whether it is a default (general) channel.
* @returns {boolean}
* @readonly
*/
get isDefaultChannel() {
return this.guild_id === this.id;
}
/**
* Gets a value indicating whether all messages were loaded.
* @returns {boolean}
* @readonly
*/
get allMessagesLoaded() {
return !this._discordie._messages.channelHasMore(this.id);
}
/**
* Creates an array of cached messages in this channel, sorted in order of
* arrival (message cache is sorted on message insertion, not when this
* getter is invoked).
*
* Returns an empty array if channel no longer exists.
*
* > **Note:** Message cache also includes deleted messages.
* > You can filter them by checking `IMessage.deleted` boolean.
* @returns {Array<IMessage>}
* @readonly
*/
get messages() {
return this._discordie.Messages.forChannel(this.id);
}
/**
* Makes a request to fetch messages for this channel.
*
* Discord API does not allow fetching more than 100 messages at once.
*
* Promise resolves with an Object with following structure:
* ```js
* {
* messages: Array<IMessage>,
* limit: Number, // same as parameter passed or default value
* before: String | null, // message id
* after: String | null // message id
* }
* ```
* @param {Number|null} [limit] - Default is 100
* @param {IMessage|String|null} [before] - Message or message id
* @param {IMessage|String|null} [after] - Message or message id
* @returns {Promise<Object, Error>}
* @example
* var guild = client.Guilds.find(g => g.name == "test");
* var channel = guild.generalChannel;
*
* // simple fetch:
* channel.fetchMessages().then(() => {
* console.log("[simple] messages in cache: " + channel.messages.length);
* });
*
* // fetching more than 100 messages into cache sequentially:
* fetchMessagesEx(channel, 420).then(() => {
* console.log("[extended] messages in cache: " + channel.messages.length);
* });
*
* // fetch more messages just like Discord client does
* function fetchMessagesEx(channel, left) {
* // message cache is sorted on insertion
* // channel.messages[0] will get oldest message
* var before = channel.messages[0];
* return channel.fetchMessages(Math.min(left, 100), before)
* .then(e => onFetch(e, channel, left));
* }
* function onFetch(e, channel, left) {
* if (!e.messages.length) return Promise.resolve();
* left -= e.messages.length;
* console.log(`Received ${e.messages.length}, left: ${left}`);
* if (left <= 0) return Promise.resolve();
* return fetchMessagesEx(channel, left);
* }
*/
fetchMessages(limit, before, after) {
if (!limit) limit = 100;
if (before) before = before.valueOf();
if (after) after = after.valueOf();
return new Promise((rs, rj) => {
rest(this._discordie).channels.getMessages(this.id, limit, before, after)
.then(e => {
e.messages = e.messages
.map(msg => this._discordie.Messages.get(msg.id));
return rs(e);
})
.catch(rj);
});
}
/**
* Creates an array of cached pinned messages in this channel.
*
* Pinned message cache is updated only if all pinned messages have been
* loaded with `ITextChannel.fetchPinned()`.
*
* Returns an empty array if channel no longer exists or if pinned messages
* have not been fetched yet.
* @returns {Array<IMessage>}
* @readonly
*/
get pinnedMessages() {
return this._discordie.Messages.forChannelPinned(this.id);
}
/**
* Makes a request to fetch pinned messages for this channel.
*
* Promise resolves with an Object with following structure:
* ```js
* {
* channelId: String,
* messages: Array<IMessage>
* }
* ```
* @returns {Promise<Object, Error>}
*/
fetchPinned() {
return new Promise((rs, rj) => {
rest(this._discordie).channels.getPinnedMessages(this.id)
.then(e => {
e.messages = e.messages
.map(msg => this._discordie.Messages.get(msg.id));
return rs(e);
})
.catch(rj);
});
}
/**
* Makes a request to send a message to this channel. Messages over 2000
* characters will be rejected by the server.
*
* Use `uploadFile` if you want to send a message with an attachment.
* @param {String|Array<String>} content
* Strings will be sent as is, arrays - joined with a newline character.
* @param {IUser|IGuildMember|Array<IUser>|Array<IGuildMember>} [mentions]
* Deprecated: left for backward compatibility.
* @param {boolean} [tts]
* @param {Object} [embed]
* Refer to [official API documentation](https://discordapp.com/developers/docs/resources/channel#embed-object)
* for embed structure description.
* @returns {Promise<IMessage, Error>}
* @example
* var guild = client.Guilds.find(g => g.name == "test");
* if (!guild) return console.log("invalid guild");
*
* var channel = guild.generalChannel;
*
* channel.sendMessage("regular message");
* channel.sendMessage("test with tts", true);
*
* var user = client.Users.find(u => u.username == "test");
* if (!user) return console.log("invalid user");
*
* channel.sendMessage("mentioning user " + user.mention);
* channel.sendMessage("@everyone or @here mention if you have permissions");
*
* channel.sendMessage("message with an embed", false, {
* color: 0x3498db,
* author: {name: "author name"},
* title: "This is an embed",
* url: "http://google.com",
* timestamp: "2016-11-13T03:43:32.127Z",
* fields: [{name: "some field", value: "some value"}],
* footer: {text: "footer text"}
* });
*/
sendMessage(content, mentions, tts, embed) {
if (Array.isArray(content)) content = content.join("\n");
if (typeof content !== "string") content = String(content);
if (!Array.isArray(mentions)) {
embed = tts;
tts = mentions;
mentions = [];
}
mentions = Utils.convertMentions(mentions);
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.createMessage(this.id, content, mentions, tts, embed)
.then(msg => rs(this._discordie.Messages.get(msg.id)))
.catch(rj);
});
}
/**
* Makes a request to send typing status for this channel.
*
* Discord client displays it for 10 seconds, sends every 5 seconds.
* Stops showing typing status if receives a message from the user.
* @returns {Promise}
*/
sendTyping() {
return rest(this._discordie).channels.postTyping(this.id);
}
/**
* Makes a request to upload data to this channel.
* Images require a `filename` with a valid extension to actually be uploaded.
* @param {Buffer|ReadableStream|String} readableStream
* Data to upload or filename as a string
* @param {String} filename
* Actual filename to show, required for non-string `readableStream`
* @param {String} [content] - Additional comment message for attachment
* @param {boolean} [tts]
* @returns {Promise<IMessage, Error>}
* @example
* channel.uploadFile(fs.readFileSync("test.png"), "test.png"); // Buffer
* channel.uploadFile(fs.createReadStream("test.png"), "test.png"); // Stream
* channel.uploadFile("test.png"); // File
* channel.uploadFile("test.png", null, "file with message");
* channel.uploadFile("test.png", null, "file with message and tts", true);
*/
uploadFile(readableStream, filename, content, mentions, tts) {
if (mentions === true) {
tts = mentions;
mentions = [];
}
mentions = Utils.convertMentions(mentions);
return new Promise((rs, rj) => {
rest(this._discordie)
.channels.uploadFile(
this.id, readableStream, filename,
content, mentions, tts
)
.then(msg => rs(this._discordie.Messages.get(msg.id)))
.catch(rj);
});
}
}
module.exports = ITextChannel;

285
node_modules/discordie/lib/interfaces/IUser.js generated vendored Normal file
View File

@ -0,0 +1,285 @@
"use strict";
const IBase = require("./IBase");
const IPermissions = require("./IPermissions");
const Utils = require("../core/Utils");
const User = require("../models/User");
const Constants = require("../Constants");
const Endpoints = Constants.Endpoints;
/**
* @interface
* @model User
* @extends IBase
* @description
* Object representing a user, user-bot, or a webhook-bot.
*
* Both bots have the `bot` property set to true.
*
* Each webhook-bot object is unique to a message and may have different
* username and avatar, the user collection only contains last seen data.
* Use `IMessage.author` to get a message-specific object.
*/
class IUser extends IBase {
constructor(discordie, userId, webhookUser) {
super();
Utils.definePrivate(this, {
_discordie: discordie,
_userId: userId,
_webhookUser: webhookUser
});
if (this.constructor == IUser) {
Object.freeze(this);
}
}
/**
* Gets date and time the account was registered (created) at.
* @returns {Date}
* @readonly
*/
get registeredAt() {
return new Date(Utils.timestampFromSnowflake(this.id));
}
/**
* Gets current JPG or GIF avatar URL.
* @returns {String|null}
* @readonly
*/
get avatarURL() {
const avatar = this.avatar;
if (!avatar) return null;
var fmt = "jpg";
if (avatar.length >= 2 && avatar[0] === "a" && avatar[1] === "_") {
fmt = "gif";
}
return Constants.CDN_ENDPOINT + Endpoints.CDN_AVATAR(this.id, avatar, fmt);
}
/**
* Gets current JPG avatar URL.
* @returns {String|null}
* @readonly
*/
get staticAvatarURL() {
const avatar = this.avatar;
if (!avatar) return null;
return Constants.CDN_ENDPOINT + Endpoints.CDN_AVATAR(this.id, avatar);
}
/**
* Current status of the user.
* @returns {String}
* @readonly
*/
get status() {
return this._discordie._presences.getStatus(this.id);
}
/**
* Current game the user is playing.
* @returns {Object|null}
* @readonly
*/
get game() {
return this._discordie._presences.getGame(this.id);
}
/**
* Name of the current game the user is playing.
* @returns {String|null}
* @readonly
*/
get gameName() {
return this.game ? this.game.name : null;
}
/**
* Previous status of the user.
* @returns {String}
* @readonly
*/
get previousStatus() {
return this._discordie._presences.getPreviousStatus(this.id);
}
/**
* Previous game the user was playing.
* @returns {Object|null}
* @readonly
*/
get previousGame() {
return this._discordie._presences.getPreviousGame(this.id);
}
/**
* Name of the previous game the user was playing.
* @returns {String|null}
* @readonly
*/
get previousGameName() {
return this.previousGame ? this.previousGame.name : null;
}
/**
* Checks whether the user is mentioned in a `message`.
* @param {IMessage} message
* @param {boolean} ignoreImplicitMentions
* @returns {boolean}
*/
isMentioned(message, ignoreImplicitMentions) {
const IMessage = require("./IMessage");
if (!message) return false;
if (!(message instanceof IMessage)) {
if (message.id) message = message.id;
message = this._discordie.Messages.get(message);
if (!message) return false;
}
return message.mentions.some(mention => mention.id === this.id) ||
(ignoreImplicitMentions === true ? false : message.mention_everyone);
}
/**
* Opens or gets existing Direct Message channel.
* @returns {Promise<IDirectMessageChannel, Error>}
*/
openDM() {
return this._discordie.DirectMessageChannels.getOrOpen(this);
}
/**
* Attempts to get a guild member interface, returns null if this user is not
* a member of the `guild` or `guild` is not in cache.
* @param {IGuild|String} guild
* @returns {IGuildMember|null}
*/
memberOf(guild) {
return this._discordie.Users.getMember(guild.valueOf(), this.id) || null;
}
/**
* Resolves permissions for user in `context`.
*
* Returns a helper object with getter boolean properties.
*
* Throws:
*
* - ** `"user must be an instance of IUser"` **
*
* If the method was called without binding to a `IUser` object
* (as a function).
*
* - ** `"context must be an instance of IChannel or IGuild"` **
*
* If context type is invalid.
*
* - ** `"Invalid context"` **
*
* If context object no longer exists in cache.
*
* - ** `"User is not a member of the context"` **
*
* If this user is not a member of the guild or
* guild the channel belongs to.
*
* See documentation of `IPermissions` for list of possible permissions.
* @param {IChannel|IGuild} context
* @returns {IPermissions}
* @example
* const guild = client.Guilds.find(g => g.name == "test");
* const channel = guild.channels.find(c => c.name == "restricted");
* const user = guild.members.find(m => m.username == "testuser");
*
* const guildPerms = user.permissionsFor(guild); // resolves to role permissions
* const channelPerms = user.permissionsFor(channel); // resolves to channel permissions
*
* console.log(guildPerms.General.MANAGE_ROLES); // false
* console.log(guildPerms.Text.READ_MESSAGES); // true
* // The `restricted` channel has `READ_MESSAGES` denied
* console.log(channelPerms.Text.READ_MESSAGES); // false
*/
permissionsFor(context) {
return IPermissions.resolve(this, context);
}
/**
* Resolves permissions for user in `context` and checks if
* user has `permission`.
*
* See `IUser.permissionsFor` method for list of throwable errors.
*
* See documentation of `IPermissions` for full list of possible permissions.
* @param {Number} permission - One or multiple permission bits
* @param {IChannel|IGuild} context
* @returns {boolean}
* @example
* const guild = client.Guilds.find(g => g.name == "test");
* const channel = guild.channels.find(c => c.name == "node_discordie");
* const user = guild.members.find(m => m.username == "testuser");
* user.can(Discordie.Permissions.General.KICK_MEMBERS, guild); // resolves to role permissions
* user.can(Discordie.Permissions.Text.READ_MESSAGES, channel); // resolves to channel permissions
*/
can(permission, context) {
return (IPermissions.resolveRaw(this, context) & permission) != 0;
}
/**
* Gets the first voice channel that member of `guild` currently in.
* @param {IGuild|String|null} guild
* Guild or an id string, null for private call
* (call channel is only available if recipient is in call with current user)
* @returns {IVoiceChannel|null}
*/
getVoiceChannel(guild) {
const state = this._discordie._voicestates.getUserStateInGuild(
(guild ? guild.valueOf() : null), this.id
);
if (!state) return null;
return this._discordie.Channels.get(state.channel_id);
}
/**
* Creates a mention from this user's id.
* @returns {String}
* @readonly
* @example
* channel.sendMessage(user.mention + ", example mention");
*/
get mention() {
return `<@${this.id}>`;
}
/**
* Creates a nickname mention from this user's id.
* @returns {String}
* @readonly
*/
get nickMention() {
return `<@!${this.id}>`;
}
/**
* Returns true if this is a non-user bot object such as webhook-bot.
* @return {boolean}
*/
get isWebhook() {
const nonUserBot =
this.bot && this.discriminator === Constants.NON_USER_BOT_DISCRIMINATOR;
return !!this._webhookUser || nonUserBot;
}
}
IUser._inherit(User, function modelPropertyGetter(key) {
if (this._webhookUser) return this._webhookUser[key];
return this._discordie._users.get(this._userId)[key];
});
module.exports = IUser;

View File

@ -0,0 +1,256 @@
"use strict";
const Constants = require("../Constants");
const StatusTypes = Constants.StatusTypes;
const Permissions = Constants.Permissions;
const ICollectionBase = require("./ICollectionBase");
const IGuild = require("./IGuild");
const IGuildMember = require("./IGuildMember");
const IChannel = require("./IChannel");
const IUser = require("./IUser");
const IDirectMessageChannel = require("./IDirectMessageChannel");
const Utils = require("../core/Utils");
/**
* @interface
* @extends ICollectionBase
*/
class IUserCollection extends ICollectionBase {
constructor(discordie, valuesGetter, valueGetter) {
super({
valuesGetter: valuesGetter,
valueGetter: valueGetter,
itemFactory: (id) => new IUser(this._discordie, id)
});
Utils.definePrivate(this, {_discordie: discordie});
}
/**
* Request members and wait until cache populates for all guilds or
* an array of guilds.
* Request is made over gateway websocket.
*
* Will request members for all guilds if no arguments passed.
*
* By default Discord sends only online members if there are more than 250
* (offline and online total) joined in a guild.
*
* Returned promise will resolve when all members have been fetched.
* Returned promise will reject if all or some members have not been received
* within 60 seconds or primary gateway websocket disconnected.
*
* If all members for chosen guilds are already in cache - returns a
* resolved promise.
*
* > **Note:** When guilds become unavailable or deleted
* > (events `GUILD_UNAVAILABLE` and `GUILD_DELETE`)
* > all members will also be deleted from cache.
* @param {IGuild|String|Array<IGuild|String>} [guilds]
* @returns {Promise}
* @example
* client.Users.fetchMembers()
* .then(() => console.log("Received members for all guilds"));
*
* client.Users.fetchMembers([guild1, guild2, guild3])
* .then(() => console.log("Received members for 3 guilds"));
*
* var p1 = client.Users.fetchMembers(guild1);
* p1.catch(err => console.log("Failed to receive guild1 members: " + err));
* var p2 = client.Users.fetchMembers(guild2);
* var p3 = client.Users.fetchMembers(guild3);
* Promise.all([p1, p2, p3])
* .then(() => console.log("Received members for 3 guilds"));
* .catch(err => console.log("Failed to receive some members: " + err));
*/
fetchMembers(guilds) {
if (guilds) {
if (guilds.map) guilds = guilds.map(guild => guild.valueOf());
else guilds = [guilds.valueOf()];
}
return this._discordie._members.fetchMembers(guilds);
}
/**
* Gets a `IGuildMember` for specified `user` of `guild`.
*
* Returns null if the user is not a member of the guild.
* @param {IGuild|String} guild - Guild or an id string
* @param {IUser|String} user - User or an id string
* @returns {IGuildMember|null}
*/
getMember(guild, user) {
guild = guild.valueOf();
user = user.valueOf();
var member = this._discordie._members.getMember(guild, user);
if (!member) return null;
return this._getOrCreateInterface(member,
() => new IGuildMember(this._discordie, user, guild)
);
}
/**
* Creates an array of `IGuildMember` for `guild`.
* @param {IGuild|String} guild - Guild or an id string
* @returns {Array<IGuildMember>}
*/
membersForGuild(guild) {
guild = guild.valueOf();
const members = [];
const guildMembers = this._discordie._members.get(guild);
if (!guildMembers) return members;
for (var member of guildMembers.values()) {
members.push(this._getOrCreateInterface(member,
() => new IGuildMember(this._discordie, member.id, guild)
));
}
return members;
}
/**
* Creates an array of `IGuildMember` that have permissions to read `channel`.
*
* > **Note:** This method computes permissions for all members and may be CPU
* > intensive for large guilds.
* @param {ITextChannel|String} channel - Channel or an id string
* @returns {Array<IGuildMember>}
*/
membersForChannel(channel) {
if (!(channel instanceof IChannel))
channel = this._discordie.Channels.get(channel);
if (!channel) return [];
const guild = channel.guild;
if (!guild) throw new Error("Channel does not exist");
const guildMembers = channel.guild.members;
if (!guildMembers) return [];
return guildMembers.filter(m =>
m.can(Permissions.Text.READ_MESSAGES, channel)
);
}
/**
* Creates an array of `IGuildMember` containing
* active members in a voice `channel`.
* @param {IVoiceChannel|String} channel - Channel or an id string
* @returns {Array<IGuildMember>}
*/
membersInVoiceChannel(channel) {
if (!(channel instanceof IChannel))
channel = this._discordie.Channels.get(channel);
if (!channel) return [];
const members = [];
const guildMembers = this._discordie._members.get(channel.guild_id);
if (!guildMembers) return members;
const userMap = this._discordie._voicestates.getStatesInChannel(channel.id);
for (var id of userMap.keys()) {
const member = guildMembers.get(id);
if (!member) continue;
members.push(this._getOrCreateInterface(member,
() => new IGuildMember(this._discordie, id, channel.guild_id)
));
}
return members;
}
/**
* Creates an array of `IUser` containing users in a private voice channel.
* @param {IDirectMessageChannel|String} channel - Channel or an id string
* @returns {Array<IUser>}
*/
usersInCall(channel) {
if (!(channel instanceof IDirectMessageChannel))
channel = this._discordie.DirectMessageChannels.get(channel);
if (!channel) return [];
const users = [];
const userMap = this._discordie._voicestates.getStatesInChannel(channel.id);
for (var id of userMap.keys()) {
const user = this.get(id);
if (user) users.push(user);
}
return users;
}
/**
* Creates an array of `IGuildMember` for `guild` that are currently online.
* @param {IGuild|String} guild - Guild or an id string
* @returns {Array<IGuildMember>}
*/
onlineMembersForGuild(guild) {
guild = guild.valueOf();
const members = [];
const presences = this._discordie._presences;
const guildMembers = this._discordie._members.get(guild);
if (!guildMembers) return members;
for (var member of guildMembers.values()) {
if (presences.getStatus(member.id) != StatusTypes.OFFLINE) {
members.push(this._getOrCreateInterface(member,
() => new IGuildMember(this._discordie, member.id, guild)
));
}
}
return members;
}
/**
* Creates an array of `IGuildMember` that
* have permissions to read `channel` and currently online.
*
* > **Note:** This method computes permissions for all members and may be CPU
* > intensive for large guilds.
* @param {ITextChannel|String} channel - Channel or an id string
* @returns {Array<IGuildMember>}
*/
onlineMembersForChannel(channel) {
return this.membersForChannel(channel).filter(
m => m.status != StatusTypes.OFFLINE
);
}
/**
* Creates an array of `IGuildMember` for `guild` that are currently offline.
*
* Does not guarantee every offline member unless
* `IUserCollection.fetchMembers` has been called for the `guild`.
*
* @param {IGuild|String} guild - Guild or an id string
* @returns {Array<IGuildMember>}
*/
offlineMembersForGuild(guild) {
guild = guild.valueOf();
const members = [];
const presences = this._discordie._presences;
const guildMembers = this._discordie._members.get(guild);
if (!guildMembers) return members;
for (var member of guildMembers.values()) {
if (presences.getStatus(member.id) == StatusTypes.OFFLINE) {
members.push(this._getOrCreateInterface(member,
() => new IGuildMember(this._discordie, member.id, guild)
));
}
}
return members;
}
/**
* Creates an array of `IGuildMember` that
* have permissions to read `channel` and currently offline.
*
* Does not guarantee every offline member unless
* `IUserCollection.fetchMembers` has been called for the guild the `channel`
* belongs to.
*
* > **Note:** This method computes permissions for all members and may be CPU
* > intensive for large guilds.
* @param {ITextChannel|String} channel - Channel or an id string
* @returns {Array<IGuildMember>}
*/
offlineMembersForChannel(channel) {
return this.membersForChannel(channel).filter(
m => m.status == StatusTypes.OFFLINE
);
}
}
module.exports = IUserCollection;

113
node_modules/discordie/lib/interfaces/IVoiceChannel.js generated vendored Normal file
View File

@ -0,0 +1,113 @@
"use strict";
const Utils = require("../core/Utils");
const User = require("../models/User");
const IChannel = require("./IChannel");
/**
* @interface
* @model Channel
* @extends IChannel
*/
class IVoiceChannel extends IChannel {
constructor(discordie, channelId) {
super(discordie, channelId);
}
/**
* Creates an array of members joined in this voice channels.
* @returns {Array<IGuildMember>}
* @readonly
*/
get members() {
return this._discordie.Users.membersInVoiceChannel(this);
}
/**
* Checks whether current user is in this voice channel.
* @returns {boolean}
* @readonly
*/
get joined() {
const vc = this._discordie.VoiceConnections;
const pendingChannel = vc.getPendingChannel(this.guild_id);
return !!(pendingChannel && pendingChannel === this._channelId);
}
/**
* Joins this voice channel.
* Creates a new voice connection if there are no active connections for
* this channels' guild.
*
* > **Note:** One account can be only in one channel per guild.
* > Promise will resolve instantly and contain the same instance
* > if connection to the server is already established.
*
* If there is a pending connection for the guild this channel belongs to,
* it will return the same promise.
*
* Checks permissions locally and returns a rejected promise with:
*
* - **`Error "Missing permission"`** if `Voice.CONNECT` permission is denied.
*
* - **`Error "Channel is full"`** if `Voice.MOVE_MEMBERS` permission is
* denied and channel is full.
*
* Returns a rejected promise with **`Error "Channel does not exist"`** if
* guild is unavailable or channel does not exist in cache.
*
* Returned promise can be cancelled or rejected: see `VOICE_DISCONNECTED`
* event for more info.
*
* @param {boolean} [selfMute]
* @param {boolean} [selfDeaf]
* @returns {Promise<VoiceConnectionInfo, Error|Number>}
*/
join(selfMute, selfDeaf) {
selfMute = !!selfMute;
selfDeaf = !!selfDeaf;
if (!this._valid)
return Promise.reject(new Error("Channel does not exist"));
// check permissions locally
// since server silently drops invalid voice state updates
if (!this.joined) {
const permissions = this._discordie.User.permissionsFor(this);
if (!permissions.Voice.CONNECT)
return Promise.reject(new Error("Missing permission"));
if (this.user_limit > 0) {
const states = this._discordie._voicestates.getStatesInChannel(this.id);
if (states.size >= this.user_limit) {
if (!permissions.Voice.MOVE_MEMBERS)
return Promise.reject(new Error("Channel is full"));
}
}
}
const vc = this._discordie.VoiceConnections;
return vc._getOrCreate(this.guild_id, this._channelId, selfMute, selfDeaf);
}
/**
* Leaves this voice channel if joined.
*/
leave() {
const info = this.getVoiceConnectionInfo();
if (info) return info.voiceConnection.disconnect();
this._discordie.VoiceConnections
.cancelIfPending(this.guild_id, this._channelId);
}
/**
* Retrieves `VoiceConnectionInfo` for this voice channel.
* @returns {VoiceConnectionInfo|null}
*/
getVoiceConnectionInfo() {
return this._discordie.VoiceConnections.getForChannel(this._channelId);
}
}
module.exports = IVoiceChannel;

View File

@ -0,0 +1,215 @@
"use strict";
const Utils = require("../core/Utils");
const ExternalEncoderFactory = require("../voice/players/ExternalEncoderFactory");
function initInterface(_interface, _options) {
if (!_interface)
throw new TypeError("Invalid interface");
if (_options) _interface.initialize(_options);
return _interface;
}
/**
* @interface
*/
class IVoiceConnection {
constructor(discordie, gatewaySocket, voiceSocket) {
// todo: ssrc to user
//discordie.Dispatcher.on(Events.VOICE_SPEAKING, e => {
// if (e.socket == voicews) this.emit("speaking", - bla - resolve ssrc)
//});
this._gatewaySocket = gatewaySocket;
this._voiceSocket = voiceSocket;
this._lastChannelId = null;
this._discordie = discordie;
Utils.privatify(this);
if (!this.canStream)
throw new Error("Failed to create IVoiceConnection");
}
dispose() {
this._lastChannelId = this.channelId;
this._gatewaySocket = null;
this._voiceSocket = null;
}
/**
* Checks whether this voice connection is no longer valid.
* @returns {boolean}
* @readonly
*/
get disposed() {
return !this._voiceSocket;
}
/**
* Checks whether this voice connection is fully initialized.
* @returns {boolean}
* @readonly
*/
get canStream() {
return this._voiceSocket && this._voiceSocket.canStream;
}
/**
* Gets channel of this voice connection.
*
* Returns last channel it was connected to if voice connection has been
* disposed.
* Returns null if guild became unavailable or channel doesn't exist in cache.
* @returns {IChannel|null}
* @readonly
*/
get channel() {
const channelId = this.channelId;
if (!channelId) return null;
return this._discordie.Channels.get(channelId);
}
/**
* Gets channel id of this voice connection.
*
* Returns last channel id it was connected to if voice connection has been
* disposed.
* @returns {String|null}
* @readonly
*/
get channelId() {
if (this._lastChannelId) return this._lastChannelId;
const gw = this._gatewaySocket;
if (!gw) return null;
const voiceState = gw.voiceStates.get(this.guildId);
if (!voiceState) return null;
return voiceState.channelId;
}
/**
* Gets guild of this voice connection.
*
* Returns null if this is a private call, or guild became unavailable or
* doesn't exist in cache.
* @returns {IGuild|null}
* @readonly
*/
get guild() {
const gw = this._gatewaySocket;
if (!gw) return null;
return this._discordie.Guilds.get(this.guildId);
}
/**
* Gets guild id of this voice connection.
*
* Returns null if this is a private call.
* @returns {String|null}
* @readonly
*/
get guildId() {
return this._voiceSocket ? this._voiceSocket.guildId : null;
}
/**
* Resolves a user object from source id assigned to this voice connection.
* @param {Number} ssrc
* @returns {IUser}
*/
ssrcToUser(ssrc) {
if (this.disposed) return null;
const userid = this._discordie._voicestates.ssrcToUserId(this, ssrc);
if (!userid) return null;
return this._discordie.Users.get(userid) || null;
}
/**
* Resolves a member object from source id assigned to this voice connection.
* @param {Number} ssrc
* @returns {IGuildMember}
*/
ssrcToMember(ssrc) {
if (this.disposed) return null;
const userid = this._discordie._voicestates.ssrcToUserId(this, ssrc);
if (!userid) return null;
return this._discordie.Users.getMember(this.guild, userid);
}
/**
* Initializes encoder and gets stream for this voice connection.
*
* Calls without arguments return existing encoder without reinitialization.
*
* See `AudioEncoder.initialize()` method for list of options.
* @param [options]
* @returns {AudioEncoderStream}
*/
getEncoderStream(options) {
const encoder = this.getEncoder(options);
if (!encoder) return null;
return encoder._stream;
}
createDecoderStream(options) {
}
/**
* Creates an external encoder.
*
* Accepts options object with `type` property (default `{type: "ffmpeg"}`).
* Each type supports additional options.
* See docs for returned classes for usage info.
* @param {Object} [options]
* @returns {FFmpegEncoder|
* OggOpusPlayer|
* WebmOpusPlayer}
*/
createExternalEncoder(options) {
if (!this._voiceSocket) return null;
return ExternalEncoderFactory.create(this, options);
}
/**
* Initializes encoder instance for this voice connection.
*
* Calls without arguments return existing encoder without reinitialization.
*
* See `AudioEncoder.initialize()` method for list of options.
* @param {Object} [options]
* @returns {AudioEncoder}
*/
getEncoder(options) {
if (!this._voiceSocket) return null;
return initInterface(this._voiceSocket.audioEncoder, options);
}
/**
* Initializes decoder instance for this voice connection.
*
* Calls without arguments return existing decoder without reinitialization.
* @param {Object} [options]
* @returns {AudioDecoder}
*/
getDecoder(options) {
if (!this._voiceSocket) return null;
return initInterface(this._voiceSocket.audioDecoder, options);
}
/**
* Disconnects this voice connection.
*/
disconnect() {
this._disconnect();
}
_disconnect(error) {
if (!this._gatewaySocket) return;
if (this.guildId || this.channelId) {
this._gatewaySocket.disconnectVoice(this.guildId, false, error);
}
}
}
module.exports = IVoiceConnection;

View File

@ -0,0 +1,230 @@
"use strict";
const Utils = require("../core/Utils");
const rest = require("../networking/rest");
function webhookToId(webhook) {
if (!webhook) throw new TypeError("Param 'webhook' is invalid");
if (typeof webhook === "string") return webhook;
return webhook.id;
}
/**
* @interface
* @description
* Wrapper for webhook methods.
*
* Example webhook object:
* ```js
* {
* "name": "abc",
* "channel_id": "78231142373424474",
* "token": "EtuNYHGkElBlE7BE266Jk...NzHvccXaUCQUOY64NbFWz9zbQ",
* "avatar": null,
* "guild_id": "78231498370026660",
* "id": "232330225768333313",
* "user": {
* "username": "testuser",
* "discriminator": "3273",
* "id": "000000000000000000",
* "avatar": null
* }
* }
* ```
*/
class IWebhookManager {
constructor(discordie) {
this._discordie = discordie;
Utils.privatify(this);
Object.freeze(this);
}
/**
* **Requires logging in with an API token.**
*
* Makes a request to get webhook objects for the specified guild.
* @param {IGuild|String} guild
* @return {Promise<Array<Object>, Error>}
*/
fetchForGuild(guild) {
guild = guild.valueOf();
return rest(this._discordie).webhooks.getGuildWebhooks(guild);
}
/**
* **Requires logging in with an API token.**
*
* Makes a request to get webhook objects for the specified channel.
* @param {IChannel|String} channel
* @return {Promise<Array<Object>, Error>}
*/
fetchForChannel(channel) {
channel = channel.valueOf();
return rest(this._discordie).webhooks.getChannelWebhooks(channel);
}
/**
* **Requires logging in with an API token.**
*
* Makes a request to create a webhook for the channel.
*
* Promise resolves with a webhook object.
* @param {IChannel} channel
* @param {Object} options
* Object with properties `{name: String, avatar: Buffer|String|null}`.
*
* String avatars must be base64 data-url encoded.
* @return {Promise<Object, Error>}
*/
create(channel, options) {
channel = channel.valueOf();
options = options || {};
if (options.avatar instanceof Buffer) {
options.avatar = Utils.imageToDataURL(options.avatar);
}
return rest(this._discordie).webhooks.createWebhook(channel, options);
}
/**
* Makes a request to fetch a webhook object.
*
* Promise resolves with a webhook object (does not contain a `user` object
* if fetched with `token` param).
* @param {Object|String} webhook - Webhook object or id.
* @param {String} token
* Webhook token, not required if currently logged in
* with an account that has access to the webhook.
* @return {Promise<Object, Error>}
*/
fetch(webhook, token) {
const webhookId = webhookToId(webhook);
return rest(this._discordie).webhooks.getWebhook(webhookId, token);
}
/**
* Makes a request to edit the specified webhook.
*
* Promise resolves with a webhook object (does not contain a `user` object
* if edited with `token` param).
* @param {Object|String} webhook - Webhook object or id.
* @param {String} token
* Webhook token, not required and can be set to null if currently logged in
* with an account that has access to the webhook.
* @param {Object} options
* Object with properties `{name: String, avatar: Buffer|String|null}`.
*
* String avatars must be base64 data-url encoded.
* @return {Promise<Object, Error>}
*/
edit(webhook, token, options) {
const webhookId = webhookToId(webhook);
options = options || {};
if (options.avatar instanceof Buffer) {
options.avatar = Utils.imageToDataURL(options.avatar);
}
return rest(this._discordie)
.webhooks.patchWebhook(webhookId, token, options);
}
/**
* Makes a request to delete the specified webhook.
* @param {Object|String} webhook - Webhook object or id.
* @param {String} token
* Webhook token, not required and can be set to null if currently logged in
* with an account that has access to the webhook.
* @return {Promise}
*/
delete(webhook, token) {
const webhookId = webhookToId(webhook);
return rest(this._discordie)
.webhooks.deleteWebhook(webhookId, token);
}
/**
* Makes a request to execute the specified webhook.
*
* > **Note:** Embeds in file uploads are not supported.
* @param {Object|String} webhook - Webhook object or id.
* @param {String} token - Required unless webhook object contains token.
* @param {Object} options
* Refer to [official API documentation](https://discordapp.com/developers/docs/)
* for more information.
* @param {boolean} [wait]
* Wait for server confirmation of message delivery,
* returned promise will contain a raw message object or an error if message
* creation failed.
* @return {Promise}
* @example
* const webhookId = "232330225768333313";
* const token = "EtuNYHGkElBlE7BE266Jk...NzHvccXaUCQUOY64NbFWz9zbQ";
* client.Webhooks.execute(webhookId, token, {content: "text message"});
* client.Webhooks.execute(webhookId, token, {
* // text message
* content: "text message",
* username: "Different Username",
* avatar_url: "https://localhost/test.png",
* tts: false,
* embeds: [{
* color: 0x3498db,
* author: {name: "who dis"},
* title: "This is an embed",
* description: "Nobody will read this anyway",
* url: "http://google.com",
* timestamp: "2016-10-03T03:32:31.205Z",
* fields: [{name: "some field", value: "some value"}],
* footer: {text: "footer text"}
* }]
* });
*
* client.Webhooks.execute(webhookId, token, {
* // file upload
* content: "text message",
* username: "Different Username",
* file: fs.readFileSync("test.png"),
* filename: "test.png"
* });
*/
execute(webhook, token, options, wait) {
const webhookId = webhookToId(webhook);
token = token || webhook.token;
return rest(this._discordie)
.webhooks.executeWebhook(webhookId, token, options, wait);
}
/**
* Makes a request to execute the specified webhook with slack-compatible
* options.
* @param {Object|String} webhook - Webhook object or id.
* @param {String} token - Required unless webhook object contains token.
* @param {Object} options
* Refer to [Slack's documentation](https://api.slack.com/incoming-webhooks)
* for more information.
*
* Discord does not support Slack's `channel`, `icon_emoji`, `mrkdwn`,
* or `mrkdwn_in` properties.
* @param {boolean} [wait]
* Wait for server confirmation of message delivery, defaults to true.
* When set to false, a message that is not saved does not return an error.
* @return {Promise}
* @example
* const webhookId = "232330225768333313";
* const token = "EtuNYHGkElBlE7BE266Jk...NzHvccXaUCQUOY64NbFWz9zbQ";
* client.Webhooks.executeSlack(webhookId, token, {
* text: "text message",
* username: "Different Username"
* });
*/
executeSlack(webhook, token, options, wait) {
const webhookId = webhookToId(webhook);
token = token || webhook.token;
return rest(this._discordie)
.webhooks.executeSlackWebhook(webhookId, token, options, wait);
}
}
module.exports = IWebhookManager;