KeyvProvider.js

  1. /*
  2. Copyright 2018 Jonah Snider
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. /**
  14. * @file Keyv provider class
  15. * @license Apache-2.0
  16. */
  17. const { SettingProvider } = require("discord.js-commando");
  18. /**
  19. * A Keyv based SettingProvider for the Discord.js Commando framework.
  20. * @see {@link https://github.com/lukechilds/keyv | Keyv GitHub}
  21. * @see {@link https://discord.js.org/#/docs/commando/master/general/welcome | Discord.js Commando documentation}
  22. * @augments SettingProvider
  23. */
  24. class KeyvProvider extends SettingProvider {
  25. /**
  26. * @param {Keyv} keyv - Keyv instance to use for the store
  27. */
  28. constructor(keyv) {
  29. super();
  30. this.keyv = keyv;
  31. this.listeners = new Map();
  32. this.client = undefined;
  33. }
  34. /**
  35. * Removes all settings in a guild
  36. * @param {Guild|string} guild - Guild to clear the settings of
  37. * @return {Promise<void>}
  38. */
  39. clear(guild) {
  40. const target = this.constructor.getGuildID(guild);
  41. this.keyv.delete(target).then(() => new Promise(resolve => resolve()));
  42. }
  43. /**
  44. * Destroys the provider, removing any event listeners.
  45. */
  46. destroy() {
  47. // Remove all listeners from the client
  48. for (const [event, listener] of this.listeners) this.client.removeListener(event, listener);
  49. this.listeners.clear();
  50. }
  51. /**
  52. * Obtains a setting for a guild
  53. * @param {Guild|string} guild - Guild the setting is associated with (or 'global')
  54. * @param {string} key - Name of the setting
  55. * @param {*} [defVal] - Value to default to if the setting isn't set on the guild
  56. * @returns {*}
  57. */
  58. async get(guild, key, defVal) {
  59. const target = this.constructor.getGuildID(guild);
  60. const settings = await this.keyv.get(target);
  61. if (settings && Object.prototype.hasOwnProperty.call(settings, key)) {
  62. // Value exists, so return it
  63. return settings[key];
  64. } else if (defVal) {
  65. // Value doesn't exist, so set it to the default value
  66. return this.set(target, key, defVal);
  67. }
  68. return undefined;
  69. }
  70. /**
  71. * Initialises the provider by connecting to databases and/or caching all data in memory.
  72. * {@link CommandoClient#setProvider} will automatically call this once the client is ready.
  73. * @param {CommandoClient} client - Client that will be using the provider
  74. */
  75. init(client) {
  76. this.client = client;
  77. (client.guilds.cache || client.guilds).forEach(async guild => {
  78. const settings = await this.keyv.get(guild.id);
  79. if (settings) {
  80. this.setupGuild(guild, settings);
  81. }
  82. });
  83. this.listeners
  84. .set("commandPrefixChange", (guild, prefix) => this.set(guild.id, "prefix", prefix))
  85. .set("commandStatusChange", (guild, command, enabled) => this.set(guild.id, `cmd-${command.name}`, enabled))
  86. .set("groupStatusChange", (guild, group, enabled) => this.set(guild.id, `grp-${group.id}`, enabled));
  87. for (const [event, listener] of this.listeners) client.on(event, listener);
  88. }
  89. /**
  90. * Removes a setting from a guild
  91. * @param {Guild|string} guild - Guild the setting is associated with (or 'global')
  92. * @param {string} key - Name of the setting
  93. * @return {Promise<*>} Old value of the setting
  94. */
  95. async remove(guild, key) {
  96. const target = this.constructor.getGuildID(guild);
  97. const prev = await this.keyv.get(target);
  98. if (prev) {
  99. const old = prev[key];
  100. delete prev[key];
  101. return new Promise(resolve => this.keyv.set(target, prev).then(() => resolve(old)));
  102. }
  103. return undefined;
  104. }
  105. /**
  106. * Sets a setting for a guild
  107. * @param {Guild|string} guild - Guild to associate the setting with (or 'global')
  108. * @param {string} key - Name of the setting
  109. * @param {*} val - Value of the setting
  110. * @return {Promise<*>} New value of the setting
  111. */
  112. async set(guild, key, val) {
  113. const target = this.constructor.getGuildID(guild);
  114. let prev = await this.keyv.get(target);
  115. if (prev === undefined) prev = {};
  116. const cur = prev;
  117. cur[key] = val;
  118. return new Promise(resolve => {
  119. if (target === "global") {
  120. this.keyv.set(target, cur, 0).then(() => resolve(val));
  121. } else {
  122. this.keyv.set(target, cur).then(() => resolve(val));
  123. }
  124. });
  125. }
  126. /**
  127. * Loads all settings for a guild
  128. * @param {string} guild - Guild ID to load the settings of (or 'global')
  129. * @param {Object} settings - Settings to load
  130. * @private
  131. */
  132. setupGuild(guild, settings) {
  133. // Load the command prefix
  134. if (settings.prefix) {
  135. if (guild) {
  136. guild._commandPrefix = settings.prefix;
  137. } else {
  138. this.client._commandPrefix = settings.prefix;
  139. }
  140. }
  141. // Load all command/group statuses
  142. for (const command of this.client.registry.commands.values()) this.setupGuildCommand(guild, command, settings);
  143. for (const group of this.client.registry.groups.values()) this.setupGuildGroup(guild, group, settings);
  144. }
  145. /**
  146. * Sets up a command's status in a guild from the guild's settings
  147. * @param {?Guild} guild - Guild to set the status in
  148. * @param {Command} command - Command to set the status of
  149. * @param {Object} settings - Settings of the guild
  150. * @private
  151. */
  152. setupGuildCommand(guild, command, settings) {
  153. if (settings[`cmd-${command.name}`] === undefined) return;
  154. if (guild) {
  155. if (!guild._commandsEnabled) guild._commandsEnabled = {};
  156. guild._commandsEnabled[command.name] = settings[`cmd-${command.name}`];
  157. } else {
  158. command._globalEnabled = settings[`cmd-${command.name}`];
  159. }
  160. }
  161. /**
  162. * Sets up a group's status in a guild from the guild's settings
  163. * @param {?Guild} guild - Guild to set the status in
  164. * @param {CommandGroup} group - Group to set the status of
  165. * @param {Object} settings - Settings of the guild
  166. * @private
  167. */
  168. setupGuildGroup(guild, group, settings) {
  169. if (settings[`grp-${group.id}`] === undefined) return;
  170. if (guild) {
  171. if (!guild._groupsEnabled) guild._groupsEnabled = {};
  172. guild._groupsEnabled[group.id] = settings[`grp-${group.id}`];
  173. } else {
  174. group._globalEnabled = settings[`grp-${group.id}`];
  175. }
  176. }
  177. }
  178. module.exports = KeyvProvider;