Compare commits

...

37 Commits
master ... v2

Author SHA1 Message Date
Kit Kasune e3eb892ee4 1st command 2 years ago
Kit Kasune 8e8df4dadc ready api 2 years ago
Kit Kasune c5eba642d0 basic message parsing 2 years ago
Kit Kasune 3bdfb83d62 startup flags 2 years ago
Kit Kasune f2d3f1eeb7 old ts stuff 2 years ago
Kit Kasune a22ac3edd2 status setting 2 years ago
Kit Kasune ca181acc42 status setting 2 years ago
Kit Kasune 1c170f0430 pretty gwadient 2 years ago
Kit Kasune dfedd09384 event loading + ready event 2 years ago
Kit Kasune fed56baec9 funni api 2 years ago
Kit Kasune 58ea237730 fix commit scripts 2 years ago
Kit Kasune b872e8e152 command loading 2 years ago
Kit Kasune df49abf989 sync loaders 2 years ago
Kit Kasune cb0d1d8622 end of console status logs 2 years ago
Kit Kasune c1d40eb1be abstract errorhandle 2 years ago
Kit Kasune 00af710ebd fix error handling for connections 2 years ago
Kit Kasune 3182903c77 login + connect 2 years ago
Kit Kasune 53a3e5c839 temp 2 years ago
Kit Kasune afcbf5ba07 oh the joys of npm 2 years ago
Kit Kasune 2456034726 basic utils 2 years ago
Kit Kasune 8018ff8dd9 skeleton the bot! 2 years ago
Kit Kasune 762bdc17bd db connection 2 years ago
Kit Kasune a5e1c4c1f9 pass extra console.log args 2 years ago
Kit Kasune 2dadce7391 ora promise wrap 2 years ago
Kit Kasune 3b3d64cf62 spinny logs 2 years ago
Kit Kasune 31d5741707 custom log types 2 years ago
Kit Kasune d469ed3969 custom log color 2 years ago
Kit Kasune a74538f085 log levels 2 years ago
Kit Kasune 1dadc3f888 loggiiggigngngngngn 2 years ago
Kit Kasune 0859e78ebd dont use em 2 years ago
Kit Kasune 9fbf59eae4 well that was a miserable idea 2 years ago
Kit Kasune cca831a86b cry over ts 2 years ago
Kit Kasune d7efeb7b16 start actual logging 2 years ago
Kit Kasune 47f113efc6 logger setup; log levels 2 years ago
Kit Kasune 36c5966dea djs 14 2 years ago
Kit Kasune c47181ea72 update packages 2 years ago
Kit Kasune 16c9eefe76 get outta here 2 years ago
  1. 5
      .gitignore
  2. 15
      .idea/Natsuki.iml
  3. 7
      .idea/discord.xml
  4. 9
      .idea/modules.xml
  5. 6
      .idea/vcs.xml
  6. 336
      .idea/workspace.xml
  7. 3
      .jshintrc
  8. 34
      .vscode/discordjs.code-snippets
  9. 217
      README.md
  10. 198
      bot.js
  11. 661
      commands/anime/anime.js
  12. 135
      commands/anime/animeimage.js
  13. 80
      commands/anime/animenick.js
  14. 716
      commands/anime/char.js
  15. 136
      commands/anime/charimage.js
  16. 29
      commands/anime/charlb.js
  17. 79
      commands/anime/charnick.js
  18. 57
      commands/anime/haswatched.js
  19. 60
      commands/anime/highvalue.js
  20. 53
      commands/anime/listanime.js
  21. 83
      commands/anime/loveani.js
  22. 84
      commands/anime/lovechar.js
  23. 103
      commands/anime/watched.js
  24. 29
      commands/anime/watchedlb.js
  25. 150
      commands/anime/watchlist.js
  26. 42
      commands/dev/admin.js
  27. 83
      commands/dev/anipulse.js
  28. 97
      commands/dev/blacklist.js
  29. 23
      commands/dev/cachesync.js
  30. 44
      commands/dev/developer.js
  31. 52
      commands/dev/eval.js
  32. 39
      commands/dev/execute.js
  33. 20
      commands/dev/logger.js
  34. 36
      commands/dev/pull.js
  35. 102
      commands/dev/reload.js
  36. 34
      commands/dev/restart.js
  37. 42
      commands/dev/setstatus.js
  38. 42
      commands/dev/staff.js
  39. 42
      commands/dev/support.js
  40. 54
      commands/dev/vip.js
  41. 36
      commands/fun/8ball.js
  42. 47
      commands/fun/bite.js
  43. 147
      commands/fun/deathnote.js
  44. 88
      commands/fun/marry.js
  45. 50
      commands/fun/nowplaying.js
  46. 181
      commands/fun/rp.js
  47. 182
      commands/fun/secretsanta.js
  48. 55
      commands/fun/slap.js
  49. 70
      commands/leveling/chests.js
  50. 29
      commands/leveling/claim.js
  51. 52
      commands/leveling/daily.js
  52. 49
      commands/leveling/ignorexp.js
  53. 44
      commands/leveling/leaderboard.js
  54. 49
      commands/leveling/levelchannel.js
  55. 98
      commands/leveling/levelrole.js
  56. 41
      commands/leveling/namemonners.js
  57. 48
      commands/leveling/setupleveling.js
  58. 122
      commands/leveling/stats.js
  59. 26
      commands/leveling/streak.js
  60. 203
      commands/misc/ar.js
  61. 52
      commands/misc/avatar.js
  62. 25
      commands/misc/commands.js
  63. 122
      commands/misc/emoji.js
  64. 118
      commands/misc/help.js
  65. 35
      commands/misc/info.js
  66. 48
      commands/misc/ingorear.js
  67. 23
      commands/misc/invite.js
  68. 40
      commands/misc/lfm.js
  69. 21
      commands/misc/mem.js
  70. 22
      commands/misc/ping.js
  71. 48
      commands/misc/prefix.js
  72. 42
      commands/misc/serverinfo.js
  73. 54
      commands/misc/sleeping.js
  74. 23
      commands/misc/supportserver.js
  75. 28
      commands/misc/uptime.js
  76. 55
      commands/misc/userinfo.js
  77. 39
      commands/moderation/autorole.js
  78. 73
      commands/moderation/ban.js
  79. 50
      commands/moderation/checkwarnings.js
  80. 58
      commands/moderation/clearwarnings.js
  81. 38
      commands/moderation/delete.js
  82. 68
      commands/moderation/kick.js
  83. 72
      commands/moderation/leave.js
  84. 94
      commands/moderation/logs.js
  85. 85
      commands/moderation/response.js
  86. 72
      commands/moderation/softban.js
  87. 55
      commands/moderation/staffrole.js
  88. 27
      commands/moderation/togglestatuses.js
  89. 33
      commands/moderation/unban.js
  90. 140
      commands/moderation/warn.js
  91. 72
      commands/moderation/welcome.js
  92. 50
      commands/social/afk.js
  93. 38
      commands/social/angry.js
  94. 61
      commands/social/bio.js
  95. 37
      commands/social/blush.js
  96. 62
      commands/social/bonk.js
  97. 26
      commands/social/clearstatus.js
  98. 56
      commands/social/creampie.js
  99. 38
      commands/social/cry.js
  100. 38
      commands/social/dance.js
  101. Some files were not shown because too many files have changed in this diff Show More

5
.gitignore vendored

@ -1,10 +1,9 @@
node_modules/
config.json
auth.json
test.js
/.idea/
api/**/*
api/
.idea/**/*
/man.js
.history/
.history/**/*

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
<excludeFolder url="file://$MODULE_DIR$/.idea/inspectionProfiles" />
<excludeFolder url="file://$MODULE_DIR$/api" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Natsukiv13" />
</component>
</module>

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Natsuki.iml" filepath="$PROJECT_DIR$/.idea/Natsuki.iml" />
<module fileurl="file://$PROJECT_DIR$/../Natsukiv13/.idea/Natsukiv13.iml" filepath="$PROJECT_DIR$/../Natsukiv13/.idea/Natsukiv13.iml" />
</modules>
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -1,336 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="826c705b-a7fa-4ece-b82a-e278fca7d494" name="Changes" comment="stats without leveling enabled">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/commands/anime/char.js" beforeDir="false" afterPath="$PROJECT_DIR$/commands/anime/char.js" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="JavaScript File" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="4aelbj7e" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="1wWIAa3TuwhYwX0w4tzIs42s0ZG" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
<property name="NEW_TOOLWINDOW_STRIPE_DEFAULTS" value="true" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="nodejs_package_manager_path" value="npm" />
<property name="settings.editor.selected.configurable" value="MTConfigurable" />
<property name="ts.external.directory.path" value="C:\Program Files\JetBrains\WebStorm 2021.2\plugins\JavaScriptLanguage\jsLanguageServicesImpl\external" />
<property name="ts.rename.search.for.dynamic.occurrences" value="true" />
<property name="vue.rearranger.settings.migration" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\clarkjr1836\Desktop\bot\Natsuki\util\ts" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\wubzy\Desktop\bot\Natsuki\slash\fun" />
</key>
</component>
<component name="RunAnythingCache">
<option name="myCommands">
<command value="npm run dev" />
<command value="git pull origin master" />
</option>
</component>
<component name="RunManager" selected="npm.dev:pretty">
<configuration name="Start" type="NodeJSConfigurationType" path-to-js-file="bot.js" working-dir="$PROJECT_DIR$">
<method v="2" />
</configuration>
<configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
<configuration name="dev:pretty" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="dev:pretty" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
<configuration name="start" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="start" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
<configuration name="start:pretty" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="start:pretty" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
<configuration name="sync" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="sync" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
<list>
<item itemvalue="Node.js.Start" />
<item itemvalue="npm.dev:pretty" />
<item itemvalue="npm.dev" />
<item itemvalue="npm.start" />
<item itemvalue="npm.start:pretty" />
<item itemvalue="npm.sync" />
</list>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="826c705b-a7fa-4ece-b82a-e278fca7d494" name="Changes" comment="" />
<created>1628567777733</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1628567777733</updated>
<workItem from="1628567780803" duration="1293000" />
<workItem from="1628630503078" duration="47000" />
<workItem from="1640123242717" duration="910000" />
<workItem from="1640124257973" duration="3978000" />
<workItem from="1640131913922" duration="1135000" />
<workItem from="1640142319793" duration="749000" />
<workItem from="1640147904557" duration="6966000" />
<workItem from="1641588609694" duration="320000" />
<workItem from="1641840125341" duration="1415000" />
<workItem from="1642537991410" duration="3454000" />
<workItem from="1642638383959" duration="700000" />
<workItem from="1642707112676" duration="1337000" />
<workItem from="1644446122031" duration="7044000" />
<workItem from="1645389860831" duration="6697000" />
<workItem from="1645490767613" duration="1990000" />
<workItem from="1645646716022" duration="11456000" />
<workItem from="1645765271756" duration="14000" />
<workItem from="1646073200451" duration="122000" />
<workItem from="1646163659329" duration="994000" />
</task>
<task id="LOCAL-00001" summary="not even gonna explain">
<created>1640148826800</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1640148826800</updated>
</task>
<task id="LOCAL-00002" summary="stoopid errors">
<created>1640149043611</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1640149043611</updated>
</task>
<task id="LOCAL-00003" summary="e">
<created>1640206937172</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1640206937172</updated>
</task>
<task id="LOCAL-00004" summary="list characters in anime">
<created>1640214318896</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1640214318896</updated>
</task>
<task id="LOCAL-00005" summary="extra images now show on char tag add">
<created>1642539309483</created>
<option name="number" value="00005" />
<option name="presentableId" value="LOCAL-00005" />
<option name="project" value="LOCAL" />
<updated>1642539309483</updated>
</task>
<task id="LOCAL-00006" summary="anime char overload prevention">
<created>1642551387709</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1642551387709</updated>
</task>
<task id="LOCAL-00007" summary="start on 0th page for anime paginations">
<created>1642551629134</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1642551629134</updated>
</task>
<task id="LOCAL-00008" summary="way more stable char commands">
<created>1642560099716</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1642560099716</updated>
</task>
<task id="LOCAL-00009" summary="SCM package scripts">
<created>1642707545761</created>
<option name="number" value="00009" />
<option name="presentableId" value="LOCAL-00009" />
<option name="project" value="LOCAL" />
<updated>1642707545761</updated>
</task>
<task id="LOCAL-00010" summary="more generous daily windows">
<created>1642708137867</created>
<option name="number" value="00010" />
<option name="presentableId" value="LOCAL-00010" />
<option name="project" value="LOCAL" />
<updated>1642708137867</updated>
</task>
<task id="LOCAL-00011" summary="slash improvements">
<created>1644449776897</created>
<option name="number" value="00011" />
<option name="presentableId" value="LOCAL-00011" />
<option name="project" value="LOCAL" />
<updated>1644449776897</updated>
</task>
<task id="LOCAL-00012" summary="more slash improvements">
<created>1644454927810</created>
<option name="number" value="00012" />
<option name="presentableId" value="LOCAL-00012" />
<option name="project" value="LOCAL" />
<updated>1644454927810</updated>
</task>
<task id="LOCAL-00013" summary="/deathnote">
<created>1644454968528</created>
<option name="number" value="00013" />
<option name="presentableId" value="LOCAL-00013" />
<option name="project" value="LOCAL" />
<updated>1644454968528</updated>
</task>
<task id="LOCAL-00014" summary="AR templating">
<created>1645501776749</created>
<option name="number" value="00014" />
<option name="presentableId" value="LOCAL-00014" />
<option name="project" value="LOCAL" />
<updated>1645501776749</updated>
</task>
<task id="LOCAL-00015" summary="n?marry">
<created>1645649572924</created>
<option name="number" value="00015" />
<option name="presentableId" value="LOCAL-00015" />
<option name="project" value="LOCAL" />
<updated>1645649572924</updated>
</task>
<task id="LOCAL-00016" summary="marriage status">
<created>1645650089290</created>
<option name="number" value="00016" />
<option name="presentableId" value="LOCAL-00016" />
<option name="project" value="LOCAL" />
<updated>1645650089290</updated>
</task>
<task id="LOCAL-00017" summary="can't marry yourself lul">
<created>1645660453432</created>
<option name="number" value="00017" />
<option name="presentableId" value="LOCAL-00017" />
<option name="project" value="LOCAL" />
<updated>1645660453432</updated>
</task>
<task id="LOCAL-00018" summary="marriage declining">
<created>1645679244004</created>
<option name="number" value="00018" />
<option name="presentableId" value="LOCAL-00018" />
<option name="project" value="LOCAL" />
<updated>1645679244004</updated>
</task>
<option name="localTasksCounter" value="19" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
<option name="oldMeFiltersMigrated" value="true" />
</component>
<component name="VcsManagerConfiguration">
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="not even gonna explain" />
<MESSAGE value="stoopid errors" />
<MESSAGE value="e" />
<MESSAGE value="list characters in anime" />
<MESSAGE value="extra images now show on char tag add" />
<MESSAGE value="anime char overload prevention" />
<MESSAGE value="start on 0th page for anime paginations" />
<MESSAGE value="way more stable char commands" />
<MESSAGE value="SCM package scripts" />
<MESSAGE value="more generous daily windows" />
<MESSAGE value="slash improvements" />
<MESSAGE value="more slash improvements" />
<MESSAGE value="/deathnote" />
<MESSAGE value="AR templating" />
<MESSAGE value="n?marry" />
<MESSAGE value="marriage status" />
<MESSAGE value="can't marry yourself lul" />
<MESSAGE value="marriage declining" />
<option name="LAST_COMMIT_MESSAGE" value="marriage declining" />
</component>
</project>

@ -1,3 +0,0 @@
{
"esversion": 9
}

@ -1,34 +0,0 @@
{
// Place your Natsuki workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
"discord.js bot command": {
"scope": "javascript,typescript",
"prefix": "djscmd",
"body": [
"const Discord = require('discord.js');${1:\n${2:\n}const $3 = require(\"$4\");}\n",
"module.exports = {",
" name: \"$5\",",
" aliases: [$6],",
" meta: {",
" category: '$7',",
" description: \"$8\",",
" syntax: '`$9`',",
" extra: ${10:null}${11:,\n guildOnly: true}",
" },${12:\n cooldown: {\n time: $13,\n silent: ${14|true,false|}${15:,\n message: \"$16\"}\n \\},}",
" help: new Discord.MessageEmbed()",
" .setTitle(\"Help -> $17\")",
" .setDescription(\"$18\")${19:\n .addField(\"$20\", \"$21\")}",
" .addField(\"Syntax\", \"`$9`\"),",
" async execute(message, msg, args, cmd, prefix, mention, client) {${22:\n if (!args.length) {return message.channel.send(`Syntax: \\`\\${prefix\\}$9\\``);\\}}",
" $0",
" }",
"};",
],
"description": "Creates a new command"
}
}

@ -1,217 +0,0 @@
# Natsuki
The official repository of Natsuki the Discord Bot.
Developed solely by WubzyGD, and intended to help others learn the ropes of discord.js, as well as provide some useful utilities to make the troubles of bot development just a little easier.
Skip down to [**Open-Source**](https://github.com/NatsukiDev/Natsuki#open-source) if you wanna get straight to ~~robbing me~~ seeing some of the awesome stuff Natsuki has to offer.
> Natsuki is now also on Discord.js' latest update, v13!
## Features
Natsuki is an anime-focused Discord bot with more than her fair share of features.
She features lots of different focuses in her commands, as well as:
- **99.99%** uptime over her year of life
- Less than ~**100-150ms** command response time
- **Active**, **open-source** development
> *Oh yeah, and a developer with no life, so she's always being updated*
Natsuki's commands and abilities include, but are absolutely not limited to:
### Moderation
- Kick/Ban/Softban
- Advanced Warning System
- Custom Welcome/Leave Messages
- Autorole/Join-role
- Mass message deletion
- WIP logging system
### Fun
- Deathnote 👀
- Last.fm API (nowplaying, etc)
- A fully-functional Secret Santa
- ~~A fortune-teller~~ 8ball
### Social
- Over 20 emotes like hug/kiss/cry/sip etc
- AFK/DnD commands
- Bio and user info
- Customizable Star Board
- Customizable auto-responses
### Utility
- Server activity monitoring
- Emoji utilities like creation/robbing
- Random number utilities
- Advanced to-do lists
- Coin-flipping
- Custom prefix
### Leveling
- Levelup messages
- Custom level message channel
- Leveling Roles
- Leaderboard
### Anime
- A growing database of anime and characters
- Ability to submit your own
- Watched/to watch/favorite anime lists
- Character favoriting
- (Soon to come) Waifu features!
## FAQ
> *Can I self-host Natuski?*
Natsuki is not meant to be self-hosted. I suppose she is indeed open-source, so if you knew what you were doing, you could build her and self-host her, but I don't condone it.
> *Can I copy Natsuki's code?*
Natuski's utilities (see below) are meant to be downloaded and copied, and some of her frameworking like command handlers are great for copying, but the point of her open-sourcing is not for you to rip apart every command and event. It's primarily for you to make use of the utilities and use the rest as a learning tool to reference and take example from.
Do note that **all** Discord bot lists **do not** allow *any* copying or forking of code. Using my utility classes and functions? Totally okay! :D Forking the repository or copy-pasting all the commands etc etc? Not coolio.
Bottom line, take what you want, but know that taking too much is not my intention and will hinder your bot's growth.
> *Can I help develop for Natsuki?*
Well gee, I thought you'd never ask! Yes! Simply make changes and submit them as a Pull Request.
If you're an active contributor, I might be able and willing to welcome you as a more permanent and official developer. Important contributors are a important part of the bot, and will be given credit for their efforts!
If developing isn't your thing, you're free to join the support server and suggest something or report a bug. I'm very very much open to suggestions and feedback.
# Open-Source
Here are some useful things to... well... make *use* of in your own code!
## Handlers/Structure
If you check out anything in the `/handle` folder, you'll find some super amazing awesome command and event loaders.
The command loader reads command files in `/commands` based on the template in `template.js`. Aliases are optional, and the help field can be a string or an embed. Commands are automatically loaded up to one subdirectory deep, meaning `commands/command.js` will load, and `commands/somefolder/command.js` will also load.
You'll have to write the commands' execution and help displays on your own, but to get them loaded into client seamlessly and effectively, this is a super strong method.
The event loader will load events from `/events`, whose file names are the discord.js event name. These are automatically placed into your client, so you shouldn't have to worry about adding your own code in this case.
## Utils
Just about anything in the utils folder could be useful for a number of reasons, but here's a few big ones:
### Tags
> util/tagfilter.js and util/tag.js
Pass in a list of Tags. Each Tag has a list of triggers, its name, then its mode.
- `toggle` = takes no options. If the tag is present, it will view as `true` in `options`.
- `append` = forms a string from the text after the tag. `-title Something Cool` will return the string `Something Cool`. Will append from all tags that trigger the alias, so if multiple `title` tags are present, all of them will be present in the same string.
- `listAppend` = Behaves like `append`, with the exception that it will return a list where each member is a string for every time the tag is used; see example below
```js
let options = new TagFilter([
new Tag(['t', '-title'], 'title', 'append'),
new Tag(['a', 'aliases', 'alts'], 'aliases', 'listAppend'),
new Tag(['f', 'force'], 'force', 'toggle')
]).test(args.join(" "));
// -title Example -a AnotherName -a Some other name -f
// options will look like this
{
title: "Example",
aliases: ["AnotherName", "Some other name"],
force: true
}
```
### Quick awaitMessages
> util/ask.js
Ask a question and wait for an answer. Returns the string of the user's response, or nothing if there was no response. **Function is asynchronous!**
*Please make sure you account for the chance of timeout.*
Pass in your `message` object, the question to ask, the time - in ms - the user has to respond, and an optional boolean of whether or not to disable the filter, which would make it so that any user can answer the question.
```js
let name = await ask(message, "What is your name?", 30000);
if (!name) {return;} // Function already sends a timeout message, just return here to stop the command from continuing.
return message.channel.send(`Hiya there, ${name}!`);
new Pagination()
```
### Pagination
> util/pagination.ts
Create a pagination based on a list of Discord MessageEmbeds.
Paginations work based off of reactions, and the pages are cycled with the click of the reaction. Pass in the message channel object, a list of embeds, the original message, and your Discord.Client object.
```js
let pages = [/*List of Discord.MessageEmbeds*/];
let help = new Pagination(message.channel, pages, message, client);
await help.setPage(1); //Pages start at 1
await help.setControllers(); //Set the reaction controllers
// OR you can call .start() to do all of this for you.
await help.start({
endTime: 60000 /*Time in ms before the pagination times out to save memory*/,
startPage: 3 /*Page num to start on*/,
user: 'discord_member_id' /*ID of a member that the Pagination will only listen to*/
}); //All of these are optional.
```
_Please note that the Pagination class is still in the works. Only one bug is currently known, and it's that the Pagination will error when a user tries to end the pagination in a DM channel, but this error is not extremely high-level and shouldn't have any major effects on your node process._
_Another note: you'll want to go into the pagination.js file and search for .setFooter() and change the name Natsuki to whatever name your bot is_
### Clean Timeout
> util/wait.js
Using Promises, wait an amount of time before you do something, but not have to deal with pesky setTimeout syntax, especially when you don't want to get way too nested with your code.
```js
const wait = require('./util/wait');
let m = await message.channel.send("hacking the mainframe...");
await wait(5000); // time is in ms
return m.edit("mainframe hacked.");
```
Or, alternatively, use `.then()` syntax so your whole code doesn't have to wait.
```js
const wait = require('./util/wait');
let m = await message.channel.send("hacking the mainframe...");
wait(5000).then(() => {return m.edit("mainframe hacked.");});
//some other stuff that will happen without waiting for the message to edit
```
### Other Utils
Have lots of free time and a good knowledge of JavaScript and discord.js?
Check out these files for some more reference and inspiration.
- `/cache/` and `/cache.js` -> A strong way to cache stuff from your database on startup.
- `/lxp/cacheloop.js` -> How I keep a cache of leveling information that is synced periodically and has a 10-minute entry expiry to save memory.
- `/makeid.js` -> Creates a hash string of your choice in length
- Anything in `/response/` -> A massive amount of code to save, parse, send, and filter responses saved by users, which encompasses things like custom embeds and messages, allowing welcome and leave messages to work.

198
bot.js

@ -1,181 +1,39 @@
const Discord = require('discord.js');
const spinnies = require('dreidels');
const gs = require('gradient-string');
const chalk = require('chalk');
const ora = require('ora');
const mongoose = require('mongoose');
const readline = require('readline');
const {SlashCommand} = require('./util/slash');
const {SlashManager} = require('./util/slashmanager');
const {SlashCommandBuilder} = require('@discordjs/builders');
const {Tag} = require('./util/tag');
const {TagFilter} = require('./util/tagfilter');
const auth = require('./src/json/auth.json');
const config = require('./src/json/config.json');
const randresp = require('./src/json/randresp.json');
const errorhandler = require('./src/util/log/errorhandler');
const flags = Discord.Intents.FLAGS;
let fl = []; Object.keys(flags).forEach(flag => fl.push(flags[flag]));
let client = new Discord.Client({intents: fl, partials: ["CHANNEL", "REACTION", "MESSAGE"]});
let botReadyResolver;
const log = require('./src/util/log/log');
client.misc = {
savers: ['497598953206841375', '480535078150340609', '468903364533420074'],
activeDMs: new Discord.Collection(),
statusPings: new Discord.Collection(),
startup: new Date(),
startupNoConnect: null,
cache: {
ar: new Map(),
arIgnore: new Map(),
bl: {
guild: [],
user: []
},
lxp: {
enabled: [],
xp: {},
hasLevelRoles: [],
disabledChannels: new Map()
},
chestsTimeout: new Map(),
chests: {
enabled: [],
timeout: new Map(),
waiting: new Map()
},
monit: {},
monitEnabled: [],
inVC: [],
VCG: {},
activeVC: [],
chars: new Discord.Collection(),
anime: new Discord.Collection(),
charsID: new Discord.Collection(),
animeID: new Discord.Collection(),
charsNum: 0,
charsLove: new Discord.Collection(),
monners: {},
lastMonners: {},
monnersNames: new Map(),
spin: new spinnies(),
rp: new Map(),
returnToSleep: new Map(),
marriageRequests: new Discord.Collection()
},
loggers: {},
rl: readline.createInterface({input: process.stdin, output: process.stdout}),
cooldown: new Discord.Collection(),
config: {
nocli: false,
dev: false,
logs: 'normal',
lightstartup: false,
ignorecmds: [],
gradients: false,
spinners: false
},
botFinished: new Promise(r => {botReadyResolver = r;}),
fullyReady: false
};
const auth = require('./auth.json');
async function init() {
const cliargs = new TagFilter([
new Tag(['cli', 'c', 'nc', 'nocli'], 'nocli', 'toggle'),
new Tag(['dev', 'd', 'development', 'test'], 'dev', 'toggle'),
new Tag(['logs', 'l', 'loglevel', 'll'], 'logs', 'append'),
new Tag(['lightstart', 'lightstartup', 'ls'], 'lightstartup', 'toggle'),
new Tag(['i', 'ignore', 'icmd', 'ignorecmd'], 'ignorecmds', 'listAppend'),
new Tag(['g', 'gradient', 'gradients'], 'gradients', 'toggle'),
new Tag(['s', 'sp', 'spinners', 'spin'], 'spinners', 'toggle')
]).test(process.argv.slice(2).join(" "));
if (Object.keys(cliargs).length) {
console.log(`${chalk.gray('[ARGS]')} >> ${chalk.gray.bold("Arguments detected.\n")}`);
Object.keys(cliargs).forEach(arg => {
client.misc.config[arg] = cliargs[arg];
console.log(`${chalk.gray('[ARGS]')} >> ${chalk.gray.bold(arg)}${chalk.gray(':')} ${chalk.blue(cliargs[arg])}`);
});
console.log('');
}
let cloginsp = ora(chalk.magentaBright('Connecting Discord client...')).start();
let pclc = new Date().getTime();
await client.login(auth.token);
cloginsp.stop(); cloginsp.clear();
console.log(`${chalk.green('[BOOT]')} >> ${chalk.greenBright(`Connected to Discord in `)}${chalk.white(`${new Date().getTime() - pclc}ms`)}`);
const flags = Discord.GatewayIntentBits;
const partials = Discord.Partials;
let fl = []; Object.keys(flags).forEach(flag => fl.push(flags[flag])); // fuck new standards i'm in't'zing with all the flags.
const client = new Discord.Client({intents: fl, partials: [partials.Channel, partials.Message, partials.Reaction]});
// a "fuck v14" counter is gonna be here real soon i can feel it.
client.misc.startupNoConnect = new Date();
client.config = auth;
const startBot = async () => {
client.config = config;
client.auth = auth;
client.config.randResp = randresp;
let mloginsp = ora(chalk.magentaBright('Connecting to Mongo client...')).start();
let pmcc = new Date().getTime();
const config = client.config;
try {
await mongoose.connect(`mongodb+srv://${config.database.user}:${config.database.password}@${config.database.cluster}.3jpp4.mongodb.net/test`, {
useFindAndModify: false, useNewUrlParser: true, dbName: 'Natsuki-Main', useUnifiedTopology: true, useCreateIndex: true
}).catch(e => {
let date = new Date(); date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6);
console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to connect to Mongo Cluster`)}`, e);
mloginsp.stop(); mloginsp.clear();
});
mloginsp.stop(); mloginsp.clear();
console.log(`${chalk.green('[BOOT]')} >> ${chalk.greenBright(`Connected to Mongo Database in `)}${chalk.white(`${new Date().getTime() - pmcc}ms`)}`);
} catch (e) {
let date = new Date(); date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6);
console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to connect to Mongo Cluster`)}`, e);
mloginsp.stop(); mloginsp.clear();
}
require('./src/util/misc/setutils')(client); // add some basic swiss army knife utils
client.developers = ["330547934951112705", "673477059904929802"];
const loggers = log(client);
Object.keys(loggers).forEach(logger => client[logger] = loggers[logger]);
client.utils = {};
client.utils.s = num => num === 1 ? '' : 's';
client.utils.as = (num, text) => `${text}${client.utils.s(num)}`;
client.utils.an = (text, caps) => `${caps ? 'A' : 'a'}${['a', 'e', 'i', 'o', 'u'].includes(text.toLowerCase().trim().slice(0, 1)) ? 'n' : ''} ${text}`;
client.utils.c = (text, a=true) => `${text.slice(0, 1).toUpperCase()}${a ? text.slice(1).toLowerCase() : text.slice(1)}`;
client.utils.ca = (text, a=true) => text.split(/\s+/gm).map(t => client.utils.c(t, a)).join(" ");
client.utils.sm = (mpr, ago=true) => `${mpr.years ? `${mpr.years} year${client.utils.s(mpr.years)} ` : ''}${mpr.months ? `${mpr.months} month${client.utils.s(mpr.months)} ` : ''}${mpr.days} day${client.utils.s(mpr.days)}${ago ? ' ago' : ''}`;
client.utils.p = (text) => text.endsWith('s') ? "'" : "'s";
client.utils.ps = (text) => `${text}${client.utils.p(text)}`;
client.log(client.utils.gr(client.config.randResp.clistart), {color: "#78d9f8", source: "NATS"}, true, true); //natsuki's wakeup log
['commands', 'aliases', 'executables'].forEach(x => client[x] = new Discord.Collection());
client.responses = {triggers: [], commands: new Discord.Collection()};
let iters = ['command', 'event', 'response'];
if (client.misc.config.spinners) {
console.log('');
client.misc.cache.spinLog = [];
iters.map(i => `Loading ${i.slice(0, 1).toUpperCase()}${i.slice(1)}s`)
.map(i => client.misc.config.gradients ? gs.instagram(i) : chalk.blue(i))
.forEach((i, ind) => client.misc.cache.spin.add(iters[ind], {text: i}));
}
require('./src/handle/startup/run/getflags')(client);
for (let i = 0; i < iters.length; i++) {let x = iters[i]; await require(`./handle/${x}`)(client);}
const spl = client.misc.config.spinners ? (i) => client.misc.cache.spinLog.push(i) : (i) => console.log(i);
spl(`\n${chalk.gray('[BOOT]')} >> ${chalk.blue('Getting Slash Commands...')}\n`);
client.slash = new SlashManager(client)
.setTestServer('691122844339404800')
.importCommands(false, undefined, (cmd) => spl(`${chalk.gray('[LOAD]')} >> ${chalk.blueBright('Loaded Slash Command')} ${chalk.white(cmd.name)}`))
.init();
if (client.misc.config.dev) {await client.slash.devRegister();}
else {await client.slash.register();}
client = client.slash.client;
spl(`\n${chalk.gray('[BOOT]')} >> ${chalk.blue('Loaded all Slash Commands')}`);
if (client.misc.config.spinners) {
client.misc.cache.spinLog.forEach(log => console.log(log));
}
if (!client.misc.config.nocli) {require('./handle/console')(client);}
client.utils.logch = async () => {return client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915');};
client.guildconfig = {};
client.guildconfig.prefixes = new Map();
client.guildconfig.logs = new Map();
botReadyResolver(0);
await require('./src/db/connect')(client); //connect to database
await require('./src/handle/startup/run/collect')(client); //load in commands and events
await require('./src/handle/startup/run/login')(client); //log in to discord
};
startBot().catch(e => errorhandler(e)); // TODO add a .catch() and flag to recover the process
// feels like there isn't a function name to do this justice :joy:
await require('./util/wait')(5000);
if (!client.misc.readied) {client.misc.forcedReady = true; await require('./events/ready')(client);}
}
init().then(() => {});
// to do list:
// TODO check log files later for cleanup, config, and util optimization
// TODO check connect file later for ^

@ -1,661 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const AniData = require('../../models/anime');
const AF = require('../../models/anifav');
const {TagFilter} = require("../../util/tagfilter");
const {Tag} = require ("../../util/tag");
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "anime",
aliases: ['ani', 'an'],
meta: {
category: 'Anime',
description: "View, submit, favorite, and rate animes",
syntax: '``',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Anime")
.setDescription("View and find anime in our huge list of anime!")
.addField("Syntax", "`anime <>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}anime <>\``);}
let queue = false;
let options = {};
let dmch;
if (['a', 'add', 'n', 'new'].includes(args[0].trim().toLowerCase())) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {
await message.channel.send("Since you aren't a Natsuki Staff member, this anime will be __submitted__ for reviewal!");
queue = true;
}
options = new TagFilter([
new Tag(['title', 't', 'name', 'n'], 'name', 'append'),
new Tag(['japname', 'japanesename', 'jn'], 'japname', 'append'),
new Tag(['description', 'desc', 'd', 'plot', 'p'], 'plot', 'append'),
new Tag(['pub', 'pubs', 'publishers', 'publisher', 'pb'], 'publishers', 'listAppend'),
new Tag(['stud', 's', 'studio', 'studs', 'studios'], 'studios', 'listAppend'),
new Tag(['began', 'airstart', 'as'], 'airStartDate', 'append'),
new Tag(['ended', 'airend', 'ae'], 'airEndDate', 'append'),
new Tag(['iscomplete', 'completed', 'ic'], 'isComplete', 'toggle'),
new Tag(['seasons', 'sns'], 'seasons', 'append'),
new Tag(['episodes', 'es'], 'episodes', 'append'),
new Tag(['genres', 'g'], 'genres', 'listAppend'),
new Tag(['tags', 'ta', 'tgs', 'tg', 'tag'], 'tags', 'listAppend'),
new Tag(['cs', 'characters', 'chars', 'chs'], 'characters', 'listAppend'),
new Tag(['streams', 'streamat', 'sa'], 'streamAt', 'listAppend'),
new Tag(['img', 'thumb', 'thumbnail', 'image'], 'thumbnail', 'append')
]).test(args.join(' '));
if (Object.keys(options).length) {
let foptions = {};
let option; for (option of Object.keys(options)) {
if (Array.isArray(options[option])) {
let s = '';
let data;
for (data of options[option]) {
s += data;
s += options[option].indexOf(data) < (options[option].length - 1) ? ', ' : '';
}
foptions[option] = s;
}
}
} else {
if (client.misc.activeDMs.has(message.author.id)) {return message.channel.send("I'm already asking you questions in a DM! Finish that first, then try this command again.");}
client.misc.activeDMs.set(message.author.id, 'anime-add');
let mesg = await message.author.send("I'm going to ask you some questions about the anime's info. Just reply with the answer and use good grammar and spelling and be as accurate as possible. To cancel the process, just leave the question unanswered for a few minutes and I'll let you know that the question timed out and is not longer answerable.")
.catch(() => {return message.reply("Something went wrong there! Most likely, your DMs are closed.");});
if (message.guild) {await message.channel.send("Check your DMs!");}
function clearDM() {client.misc.activeDMs.delete(message.author.id);}
dmch = mesg.channel;
options.name = await ask(mesg, "What is the anime's name? Make sure to use proper capitalization and spelling. This applies to the rest of the questions I'll ask you.", 60000, true); if (!options.name) {return;}
if (options.name.length > (queue ? 75 : 250)) {clearDM(); return dmch.send(`The anime name can't be more than ${queue ? 75 : 250} characters!`);}
options.plot = await ask(mesg, "How would you describe the anime? Give a very brief description of things like its plot, main characters, and setting.", 240000, true); if (!options.plot) {return clearDM();}
if (options.plot.length > 500) {clearDM(); return dmch.send("Oi! I said give a \"very brief\" description of the anime!");}
options.japname = await ask(mesg, "What is the anime's japanese name? (The romanization, not the Japanese characters.)", 120000, true); if (!options.japname) {return clearDM();}
if (options.japname.length > 75) {clearDM(); return dmch.send("The japanese name can't be more than 75 characters!");}
options.studios = await ask(mesg, "What studio created the anime? If there are multiple studios, please separate them with a comma.", 120000, true); if (!options.studios) {return clearDM();}
if (options.studios.length > 150) {clearDM(); return dmch.send("No way there were actually that many studios...");}
options.studios = options.studios.split(/,\s+/gm);
if (options.studios.length > 5) {clearDM(); return dmch.send("No way there were actually that many studios...");}
options.publishers = await ask(mesg, "What company published the anime? If there are multiple publishers, please separate them with a comma.", 120000, true); if (!options.publishers) {return clearDM();}
if (options.publishers.length > 150) {clearDM(); return dmch.send("No way there were actually that many publishers...");}
options.publishers = options.publishers.split(/,\s+/gm);
if (options.publishers.length > 5) {clearDM(); return dmch.send("No way there were actually that many publishers...");}
options.isComplete = await ask(mesg, "Is the anime completed? (If the most recent season has finished, you may only say \"no\" if the next season is *confirmed* by the *studio or publishers* or the next season is in the works.", 60000, true); if (!options.isComplete) {return clearDM();}
if (!['y', 'yes', 'ye', 'n', 'no'].includes(options.isComplete.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
options.isComplete = ['y', 'yes', 'ye'].includes(options.isComplete.trim().toLowerCase());
//options.airStartDate = await ask(mesg, "When did the anime start? Please format this as \"Month Day, Year\" - e.g. January 1, 2021", 120000, true); if (!options.airStartDate) {return clearDM();}
//if (options.isComplete) {options.airEndDate = await ask(mesg, "When did the anime end?", 120000, true); if (!options.airEndDate) {return clearDM();}}
options.seasons = await ask(mesg, "How many seasons does the anime have? Please don't include spinoffs. (Gun Gale Online spinoff doesn't count as a Sword Art Online season.)", 120000, true); if (!options.seasons) {return clearDM();}
if (isNaN(options.seasons) || Number(options.seasons < 1)) {clearDM(); return dmch.send("You either didn't give a number, or it was < 1.");}
options.seasons = Number(options.seasons);
options.episodes = await ask(mesg, "How many episodes does the anime have? Try not to include OVAs or spinoffs, unless they're canon and widely considered by the community to be a part of the series.", 120000, true); if (!options.episodes) {return clearDM();}
if (isNaN(options.episodes) || Number(options.episodes < 1)) {clearDM(); return dmch.send("You either didn't give a number, or it was < 1.");}
options.episodes = Number(options.episodes);
options.genres = await ask(mesg, "What genre(s) describe the anime? If there are multiple genres, please separate them with a comma.", 120000, true); if (!options.genres) {return clearDM();}
if (options.genres.length > 200) {clearDM(); return dmch.send("That's too many genres!");}
options.genres = options.genres.split(/,\s+/gm);
if (options.genres.length > 10) {clearDM(); return dmch.send("That's too many genres!");}
/*options.tags = await ask(mesg, "What tags describe the anime? Please separate tags with a comma, and *do not put the # in the tag!*.", 120000, true); if (!options.tags) {return clearDM();}
if (options.tags.length > 200) {clearDM(); return dmch.send("That's too many tags!");}
options.tags = options.tags.split(/,\s+/gm);
if (options.tags.length > 25) {clearDM(); return dmch.send("That's too many tags!");}*/
options.streamAt = await ask(mesg, "What streaming services can you use to watch the anime? If there are multiple stream sites, please separate them with a comma. Please include only legal, licensed industries such as Netflix, Funimation, Crunchyroll, or Hulu. 9anime, cartooncrazy, GoGoAnime, and similar sites are illegal and won't be listed here.", 120000, true); if (!options.streamAt) {return clearDM();}
if (options.streamAt.length > 150) {clearDM(); return dmch.send("No way there are actually that many streaming sites to watch the anime on...");}
options.streamAt = options.streamAt.split(/,\s+/gm);
if (options.streamAt.length > 8) {clearDM(); return dmch.send("No way there are actually that many streaming sites to watch the anime on...");}
options.thumbnail = await ask(mesg, "Give me an image to use for the anime. You may upload an image or send a URL to an image **that is hosted on Discord**. Please do not upload NSFW or copyrighted images.", 120000, true); if (!options.thumbnail) {return clearDM();}
if (options.thumbnail.length > 350) {clearDM(); return dmch.send("That URL is a bit too long. Consider uploading it to imgur or another image sharing site and trying again.");}
options.characters = [];
clearDM();
}
let am;
let foptions = {};
let option; for (option of Object.keys(options)) {
if (Array.isArray(options[option])) {
let s = '';
let data;
for (data of options[option]) {
s += data;
s += options[option].indexOf(data) < (options[option].length - 1) ? ', ' : '';
}
foptions[option] = s;
}
}
if (!options.characters) {options.characters = [];}
let amEmbed = new Discord.MessageEmbed()
.setTitle(`New Anime -> ${options.name}`)
.setDescription(`${queue ? 'Requested' : 'Added'} by ${message.author.tag}`)
.addField('Info', `**Name:** ${options.name}\n**Japanese Name:** ${options.japname}\n\n**Publishers:** ${foptions.publishers}\n**Studios:** ${foptions.studios}`)
.addField('Description', options.plot)
.addField('Length', `**# of Seasons:** ${options.seasons}\n**# of Episodes:** ${options.episodes}`)
//.addField('Airing', `**Began:** ${options.airStartDate}\n**Ended:** ${options.isComplete ? options.airEndDate : 'This anime is still airing!'}`)
.addField('Other', `**Genre(s):** ${foptions.genres}\n**Characters:** ${foptions.characters}\n**Stream this at:** ${foptions.streamAt}`)
.setColor("c375f0")
.setImage(options.thumbnail)
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
try {
am = await dmch.send({embeds: [amEmbed]});
await am.react('👍');
await am.react('👎');
} catch {return dmch.send(":thinking: hmmm... something went wrong there. I might not have permissions to add reactions to messages, and this could be the issue.");}
try {
let rc = am.createReactionCollector({filter: (r, u) => ['👍', '👎'].includes(r.emoji.name) && u.id === message.author.id, max: 1, time: 60000});
rc.on("collect", async r => {
if (r.emoji.name !== '👎') {
while (true) {options.id = require('../../util/makeid')(4); if (!await AniData.findOne({id: options.id})) {break;}}
if (!queue) {
amEmbed.addField("Reviewed", `Reviewed and submitted by <@${message.author.id}>`);
client.misc.cache.anime.set(options.name, options.id);
client.misc.cache.anime.set(options.japname, options.id);
client.misc.cache.animeID.set(options.id, options.name);
}
else {amEmbed.addField("ID", options.id);}
amEmbed.setAuthor({name: !queue ? "Anime Added" : "Anime Submitted", iconURL: message.author.displayAvatarURL()});
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177823630762014').send({embeds: [amEmbed]}));
if (!queue) {options.queued = false;}
await new AniData(options).save();
return message.author.send(`Your anime has been ${!queue ? "added" : "submitted"}`);
} else {
return message.author.send("Oh, okay. I'll discard that then!");
}
});
rc.on("end", collected => {if (!collected.size) {return message.author.send("Looks like you ran out of time! Try again?");}});
} catch {return message.author.send("Hmm... there was some kind of error when I tried to submit that anime. Try again, and if it keeps not working, then go yell at my devs!");}
return;
}
if (['e', 'edit'].includes(args[0].toLowerCase())) {
args.shift();
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {return await message.channel.send("Since you aren't a Natsuki Staff member, you can't edit anime.");}
if (!args.length) {return message.channel.send("You have to provide tags to edit anime with.");}
options = new TagFilter([
new Tag(['title', 't', 'name', 'n'], 'name', 'append'),
new Tag(['rn', 'rename'], 'rename', 'append'),
new Tag(['japname', 'japanesename', 'jn'], 'japname', 'append'),
new Tag(['description', 'desc', 'd', 'plot', 'p'], 'plot', 'append'),
new Tag(['pub', 'pubs', 'publishers', 'publisher', 'pb'], 'publishers', 'listAppend'),
new Tag(['stud', 's', 'studio', 'studs', 'studios'], 'studios', 'listAppend'),
new Tag(['began', 'airstart', 'as'], 'airStartDate', 'append'),
new Tag(['ended', 'airend', 'ae'], 'airEndDate', 'append'),
new Tag(['iscomplete', 'completed', 'ic'], 'isComplete', 'toggle'),
new Tag(['seasons', 'sns'], 'seasons', 'append'),
new Tag(['episodes', 'es'], 'episodes', 'append'),
new Tag(['genres', 'g'], 'genres', 'listAppend'),
new Tag(['tags', 'ta', 'tgs', 'tg', 'tag'], 'tags', 'listAppend'),
new Tag(['streams', 'streamat', 'sa'], 'streamAt', 'listAppend'),
new Tag(['img', 'thumb', 'thumbnail', 'image'], 'thumbnail', 'append')
]).test(args.join(' '));
if (!options.name) {
let tempani = await ask(message, "What anime would you like to edit?", 60000, false, true);
if (!tempani) {return;}
options.name = tempani.split(/\s+/g);
}
let asr = await ans(message, client, options.name.toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {fn = asr.id;}
let ani = await AniData.findOne({id: fn});
if (!ani) {return message.channel.send("\\*Head scratching* that anime seems to have vanished into thin air. Try again or go yell at my devs.");}
Object.keys(options).forEach(o => {if (![undefined, null].includes(options[o]) && o !== 'name') {
if (o === 'japname') {
client.misc.cache.anime.delete(ani.japname);
client.misc.cache.anime.set(options.japname, ani.id);
}
if (Array.isArray(options[o])) {options[o].forEach(i => ani[o].push(i));}
else {ani[o] = options[o];}
ani.markModified(o);
if (o === 'rename') {
delete ani.rename;
client.misc.cache.animeID.set(ani.id, options.rename);
client.misc.cache.anime.delete(ani.name);
client.misc.cache.anime.set(options.rename, ani.id);
ani.name = options.rename;
}
}});
ani.save();
return message.channel.send('Anime updated.');
}
if (['s', 'search'].includes(args[0].trim().toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to search for?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase());
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
} else {
await message.channel.send({embeds: [asr.embed]});
}
return;
}
if (['v', 'view'].includes(args[0].trim().toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to view?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700);
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
} else {
await message.channel.send({embeds: [asr.embed]});
}
return;
}
if (['reject'].includes(args[0].trim().toLowerCase())) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {await message.channel.send("Since you aren't a Natsuki Staff member, you can't reject anime submissions!");}
let tr = await AniData.findOne({id: args[1].toLowerCase()});
if (!tr) {return message.reply("That anime submission doesn't seem to exist!");}
if (tr.queued !== true) {return message.reply("That anime was already accepted, so you can't reject it.");}
return await AniData.deleteOne({id: args[1].toLowerCase()})
.then(() => {return message.channel.send("I got that submission out of here!");})
.catch(() => {return message.reply("It seems that submission wasn't deleted for some reason. \*insert head scratching*");});
}
if (['accept', 'approve'].includes(args[0].toLowerCase())) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {await message.channel.send("Since you aren't a Natsuki Staff member, you can't accept anime submissions!");}
let tr = await AniData.findOne({id: args[1].toLowerCase()});
if (!tr) {return message.reply("That anime submission doesn't seem to exist!");}
if (tr.queued !== true) {return message.reply("That anime was already accepted, so you can't accept it again...");}
tr.queued = false;
return await tr.save()
.then(() => {
client.misc.cache.anime.set(ani.japname.normalize("NFD").replace(/[\u0300-\u036f]/g, ""), ani.id);
client.misc.cache.anime.set(ani.name.normalize("NFD").replace(/[\u0300-\u036f]/g, ""), ani.id);
if (ani.altNames) {ani.altNames.forEach(altName => client.misc.cache.anime.set(altName, ani.id));}
client.misc.cache.animeID.set(ani.id, ani.name.normalize("NFD").replace(/[\u0300-\u036f]/g, ""));
client.misc.cache.animeLove.set(ani.id, ani.watchers);
client.misc.cache.animeNum++;
client.guilds.cache.get('762707532417335296').channels.cache.get('932177823630762014').send({embeds: [new Discord.MessageEmbed()
.setTitle(`Anime Accepted -> ${tr.name}`)
.setAuthor({name: 'Anime Approved', iconURL: message.author.displayAvatarURL()})
.setThumbnail(tr.thumbnail)
.setDescription(`${tr.name} has been approved, and is now available to all Natsuki users.`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]}).catch(() => {});
return message.channel.send("I've accepted that submission.");
})
.catch(() => {return message.reply("It seems that submission wasn't accepted for some reason. \*insert head scratching*");});
}
if (['r', 'rand', 'random', 'any'].includes(args[0].toLowerCase())) {
let asr = await ans(message, client, client.misc.cache.anime.random(), -100000);
return await message.channel.send({embeds: [asr.embed]});
}
if (['w', 'watched'].includes(args[0].toLowerCase())) {
args.shift();
if (args[0] && ['v', 'view'].includes(args[0].toLowerCase())) {
if (mention) {args.shift();}
let user = mention || message.author;
let af = await AF.findOne({uid: user.id});
if (!af || !af.watched.length) {return message.channel.send(`${user.id === message.author.id ? "You haven't" : "That person hasn't"} watched any anime.`);}
let pages = [];
let pl = (Math.floor(af.watched.length / 10) + 1);
for (let i = 0; i < pl; i++) {
let s = '';
for (let x = 0; x < 10; x++) {
if (!af.watched[(i * 10) + x]) {break;}
s += `**${x + (i * 10) + 1}.** ${client.misc.cache.animeID.get(af.watched[(i * 10) + x])}\n`;
}
pages.push(new Discord.MessageEmbed()
.setAuthor({
name: message.guild ? message.guild.members.cache.get(user.id).displayName : user.username,
iconURL: message.guild ? message.guild.members.cache.get(user.id).displayAvatarURL({dynamic: true}) : user.displayAvatarURL({dynamic: true})
})
.setTitle("Finished Anime List")
.setDescription(s)
.setColor('c375f0')
.setTimestamp()
);
}
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send({embeds: [pages[0].setFooter({text: "Natsuki"})]});}
}
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to your finished list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the anime you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the anime you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
let af = await AF.findOne({uid: message.author.id}) || new AF({uid: message.author.id});
if (af.watched.includes(fn)) {return message.channel.send("Looks like that anime is already on your finished list!");}
let tfc = await AniData.findOne({id: fn});
tfc.watchers += 1;
tfc.markModified('watchers');
af.watched.push(fn);
af.markModified('watched');
let dw = false;
if (af.toWatch.includes(fn)) {
af.toWatch.splice(af.toWatch.indexOf(fn), 1);
af.markModified('toWatch');
tfc.listed--;
tfc.save();
dw = true;
}
af.save();
tfc.save();
return message.channel.send(`I've added **${tfc.name}** to your list of finished animes!${dw ? " I've also removed it from your watch list for you :p" : ''}`);
}
if (['nn', 'nickname', 'altname', 'nick', 'an'].includes(args[0].toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to add an alternate name to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This alternate name will be __submitted__ for reviewal.");
queue = true;
}
let ch = await AniData.findOne({id: fn});
if (!ch.altNames) {ch.altNames = [];}
let nn = await ask(message, `What nickname would you like to add to ${ch.name}?`, 60000, false, true);
if (!nn) {return;}
if (ch.altNames.map(nickn => nickn.toLowerCase()).includes(nn.toLowerCase())) {return message.channel.send(`Looks like **${ch.name}** already has the nickname "${nn}".`);}
if (!queue) {
ch.altNames.push(nn);
ch.markModified('altNames');
ch.save();
client.misc.cache.anime.set(nn, ch.id);
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177814638186516').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Anime Alt Name ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\``)
.addField("Name", nn)
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Anime alt name ${queue ? "submitted" : "added"}.`);
}
if (['i', 'im', 'img', 'image'].includes(args[0].toLowerCase())) {
args.shift();
let list = false;
if (args[0] && ['l', 'list'].includes(args[0].toLowerCase())) {
list = true;
args.shift();
}
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to add an image to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This image will be __submitted__ for reviewal.");
queue = true;
}
let ch = await AniData.findOne({id: fn});
if (!ch.images) {ch.images = [];}
if (list) {
if (!ch.images) {ch.images = [];}
ch.images.push(ch.thumbnail);
let pages = ch.images.map(im => new Discord.MessageEmbed()
.setTitle(ch.name)
.setDescription(`**Name:** ${ch.name}`)
.setColor("c375f0")
.setImage(im)
);
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send(pages[0].setTimestamp());}
} else {
args.shift();
let images = [];
if (message.attachments.size > 1) {
Array.from(message.attachments.keys()).forEach(i => images.push(message.attachments.get(i).url));
} else {
let tempchar = message.attachments.size
? message.attachments.first().url
: await ask(message, `Please paste the image or a link to the image you'd like to add to **${ch.name}**.`, 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
images.push(args.join(" "));
}
let f;
images.forEach(img => {if (!img.match(/^https:\/\/(?:[\w\-].?)+[\/\w\-%()_]+\.(?:png|jpg|jpeg|gif|webp)$/gm)) {f = true; return message.channel.send("I don't think that's an image. Try again?");}})
if (f) {return;}
if (images.length === 1) {
let img = images[0];
if (!queue) {
ch.images.push(img);
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Anime Image ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}**`)
.setThumbnail(ch.thumbnail)
.setImage(img)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Anime image ${queue ? "submitted" : "added"} to **${ch.name}**.`);
} else {
if (!queue) {
images.forEach(img => ch.images.push(img));
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Anime Images ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}**`)
.addField("Images", images.map(img => `${img}\n`).join(""))
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Anime images (${images.length}) ${queue ? "submitted" : "added"} to **${ch.name}**.`);
}
}
}
if (['l', 'love', 'favorite', 'fav'].includes(args[0].toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to your favorites list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the anime you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the anime you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
let cf = await AF.findOne({uid: message.author.id}) || new AF({uid: message.author.id});
if (cf.liked.includes(fn)) {return message.channel.send("Looks like that anime is already on your loved list!");}
let tfc = await AniData.findOne({id: fn});
tfc.liked += 1;
tfc.save();
cf.liked.push(fn);
cf.markModified('liked');
cf.save();
return message.channel.send(`I've added **${tfc.name}** to your loved/favorited anime list!`);
}
if (['loved', 'favorites', 'favs', 'ls', 'fs'].includes(args[0].toLowerCase())) {
let cf = await AF.findOne({uid: mention ? mention.id : message.author.id});
if (!cf || !cf.liked.length) {return message.channel.send(`Looks like ${mention ? 'they' : 'you'} haven't favorited any anime!`);}
let chars = cf.liked;
chars = chars.map(tc => client.misc.cache.animeID.get(tc));
let n = mention ? message.guild ? message.mentions.members.first().displayName : message.mentions.users.first().username : message.guild ? message.member.displayName : message.author.username;
return message.channel.send({embeds: [
new Discord.MessageEmbed()
.setAuthor({name: `${n}${n.endsWith('s') ? '' : "'s"} Favorited Anime`, iconURL: mention ? mention.displayAvatarURL() : message.author.displayAvatarURL()})
.setDescription(`**${chars.length} anime favorited**\n\n${chars.join(", ")}`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
}
}
};

@ -1,135 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const AniData = require('../../models/anime');
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "animeimage",
aliases: ['ai', 'aniimage', 'aimg'],
meta: {
category: 'Anime',
description: "Add an image to an anime",
syntax: '`animeimage <anime> <imageURL|attachedImage>` or `animeimage <list> <anime>`',
extra: null,
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Anime Images")
.setDescription("Add an image to a anime, or list the animes' images.")
.addField("Notice", "If you're not a member of Natsuki staff, this image will be submitted for reviewal.")
.addField("Syntax", "`animeimage <anime> <imageURL|attachedImage>` or `animeimage <list> <anime>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
let list = false;
if (args[0] && ['l', 'list'].includes(args[0].toLowerCase())) {
list = true;
args.shift();
}
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to add an image to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This image will be __submitted__ for reviewal.");
queue = true;
}
let ch = await AniData.findOne({id: fn});
if (!ch.images) {ch.images = [];}
if (list) {
if (!ch.images) {ch.images = [];}
ch.images.push(ch.thumbnail);
let pages = ch.images.map(im => new Discord.MessageEmbed()
.setTitle(ch.name)
.setDescription(`**Name:** ${ch.name}`)
.setColor("c375f0")
.setImage(im)
);
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send(pages[0].setTimestamp());}
} else {
args.shift();
let images = [];
if (message.attachments.size > 1) {
Array.from(message.attachments.keys()).forEach(i => images.push(message.attachments.get(i).url));
} else {
let tempchar = message.attachments.size
? message.attachments.first().url
: await ask(message, `Please paste the image or a link to the image you'd like to add to **${ch.name}**.`, 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
images.push(args.join(" "));
}
let f;
images.forEach(img => {if (!img.match(/^https:\/\/(?:[\w\-].?)+[\/\w\-%()_]+\.(?:png|jpg|jpeg|gif|webp)$/gm)) {f = true; return message.channel.send("I don't think that's an image. Try again?");}})
if (f) {return;}
if (images.length === 1) {
let img = images[0];
if (!queue) {
ch.images.push(img);
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Anime Image ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}**`)
.setThumbnail(ch.thumbnail)
.setImage(img)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Anime image ${queue ? "submitted" : "added"} to **${ch.name}**.`);
} else {
if (!queue) {
images.forEach(img => ch.images.push(img));
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Anime Images ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}**`)
.addField("Images", images.map(img => `${img}\n`).join(""))
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Anime images (${images.length}) ${queue ? "submitted" : "added"} to **${ch.name}**.`);
}
}
}
};

@ -1,80 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const AniData = require('../../models/anime');
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "animenick",
aliases: ['aninick', 'an', 'animenickname', 'ann', 'aninn'],
meta: {
category: 'Anime',
description: "Add a nickname/alternate name to an anime",
syntax: '`animenick <anime>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Anime Nicknames")
.setDescription("Add a nickname or alternate name to an anime that it can later be found by.")
.addField("Notice", "You'll first need to state the anime name, then I'll ask you for the alternate name you want to add. Don't send it in the same message as the command. It won't work.")
.addField("Syntax", "`animenick <anime>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to add an alternate name to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This alternate name will be __submitted__ for reviewal.");
queue = true;
}
let ch = await AniData.findOne({id: fn});
if (!ch.altNames) {ch.altNames = [];}
let nn = await ask(message, `What nickname would you like to add to ${ch.name}?`, 60000, false, true);
if (!nn) {return;}
if (ch.altNames.map(nickn => nickn.toLowerCase()).includes(nn.toLowerCase())) {return message.channel.send(`Looks like **${ch.name}** already has the nickname "${nn}".`);}
if (!queue) {
ch.altNames.push(nn);
ch.markModified('altNames');
ch.save();
client.misc.cache.anime.set(nn, ch.id);
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177814638186516').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Anime Alt Name ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\``)
.addField("Name", nn)
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Anime alt name ${queue ? "submitted" : "added"}.`);
}
};

@ -1,716 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const Char = require('../../models/char');
const AniData = require('../../models/anime');
const CF = require('../../models/charfav');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
const chs = require('../../util/anime/charsearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "char",
aliases: ['ch', 'character'],
meta: {
category: 'Anime',
description: "Get info on and view anime characters",
syntax: '`char <add|view|search|random|love|loved>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Characters")
.setDescription("Incomplete command, please stand by <3")
.addField("Syntax", "`char <add|view|search|random|love|loved>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}char <add|view|search|random|love|loved>\``);}
let queue = false;
let forceAni = false;
let options = {};
let dmch;
if (['a', 'add', 'n', 'new'].includes(args[0])) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {
await message.channel.send("Since you aren't a Natsuki Staff member, this character will be __submitted__ for reviewal!");
queue = true;
}
options = new TagFilter([
new Tag(['name', 'n'], 'name', 'append'),
new Tag(['description', 'desc', 'p', 'personality'], 'personality', 'append'),
new Tag(['anime', 'ani', 'a'], 'anime', 'append'),
new Tag(['thumb', 'thumbnail', 't'], 'thumbnail', 'append'),
new Tag(['img', 'image', 'i'], 'images', 'listAppend'),
new Tag(['loveInterest', 'dating', 'married', 'li'], 'loveInterest', 'append'),
new Tag(['gender', 'g', 'sex'], 'gender', 'append'),
new Tag(['nickname', 'nn', 'nick'], 'nicknames', 'listAppend'),
new Tag(['force', 'f'], 'force', 'toggle')
]).test(args.slice(1).join(' '));
if (Object.keys(options).length) {
if (message.attachments.size) {
if (options.thumbnail) {
if (!options.images) {options.images = [];}
Array.from(message.attachments.keys()).forEach(i => options.images.push(message.attachments.get(i).url));
} else {
if (!options.images) {options.images = [];}
let att = Array.from(message.attachments.keys());
if (message.attachments.size > 1) {
for (let i = 1; i < att.length; i++) {
options.images.push(message.attachments.get(att[i]).url);
}
}
options.thumbnail = message.attachments.get(att[0]).url;
}
}
let foptions = {};
let option; for (option of Object.keys(options)) {
if (Array.isArray(options[option])) {
let s = '';
let data;
for (data of options[option]) {
s += data;
s += options[option].indexOf(data) < (options[option].length - 1) ? ', ' : '';
}
foptions[option] = s;
}
}
if (!options.name || !options.anime || !options.gender || !options.thumbnail) {return message.channel.send("You're missing an option! If you don't know what this means, just run this command without any text after it.");}
let fn;
let asr = await ans(message, client, options.anime.trim().toLowerCase(), undefined, 0);
if (asr === 0) {return message.channel.send("That search returned no results! Try remaking the character?");}
else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.channel.send("Looks like you didn't find the anime you were looking for, so I went ahead and ended the character creation for you.");}
let collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.channel.send("Looks like you didn't find the anime you were looking for, so I went ahead and ended the character creation for you.");}
} else {fn = asr.id;}
options.anime = fn;
} else {
if (client.misc.activeDMs.has(message.author.id)) {return message.channel.send("I'm already asking you questions in a DM! Finish that first, then try this command again.");}
client.misc.activeDMs.set(message.author.id, 'char-add');
let mesg = await message.author.send("I'm going to ask you some questions about the character's info. Just reply with the answer and use good grammar and spelling and be as accurate as possible. To cancel the process, just leave the question unanswered for a few minutes and I'll let you know that the question timed out and is not longer answerable.")
.catch(() => {return message.reply("Something went wrong there! Most likely, your DMs are closed.");});
if (message.guild) {await message.channel.send("Check your DMs!");}
function clearDM() {client.misc.activeDMs.delete(message.author.id);}
dmch = mesg.channel;
options.name = await ask(mesg, "What is the character's name?", 60000, true); if (!options.name) {return;}
if (options.name.length > 75) {clearDM(); return dmch.send("The character name can't be more than 75 characters!");}
options.anime = await ask(mesg, "What anime (or game) did the character appear in? If the character is an OC, say 'none'", 60000, true); if (!options.anime) {return;}
if (options.anime.length > 75) {clearDM(); return dmch.send("The anime name can't be more than 75 characters!");}
if (options.anime.trim().toLowerCase() === 'none') {options.anime = null;}
else {
let fn;
let asr = await ans(mesg, client, options.anime.trim().toLowerCase());
if (asr === 0) {
let conf = await ask(mesg, "That search returned no results. Would you like me to put the anime you specified down anyways? Otherwise, I'll abandon this process, and we can try again.", 60000, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
if (!conf) {
clearDM();
return dmch.send("Okay! You can try remaking the character if you'd like.");
} else {
forceAni = true;
fn = options.anime;
}
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await dmch.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return dmch.send("Looks like you didn't find the anime you were looking for, so I went ahead and ended the character creation for you.");}
let collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return dmch.send("Looks like you didn't find the anime you were looking for, so I went ahead and ended the character creation for you.");}
} else {
await dmch.send({embeds: [asr.embed]});
let conf = await ask(mesg, "Is this the anime you meant?", 60000, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
if (!conf) {return mesg.channel.send("Well, I've got nothing, then. If that doesn't match the anime you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
options.anime = fn;
}
options.gender = await ask(mesg, "What is the character's gender?", 60000, true); if (!options.name) {return;}
if (options.gender.length > 75) {clearDM(); return dmch.send("That's one heck of a gender. Maybe like... abbreviate?");}
options.thumbnail = await ask(mesg, "Give me an image **URL** *do not upload the image* to use for the character", 120000, true); if (!options.thumbnail) {return clearDM();}
if (options.thumbnail.length > 350) {clearDM(); return dmch.send("That URL is a bit too long. Consider uploading it to imgur or another image sharing site and trying again.");}
options.characters = [];
clearDM();
let foptions = {};
let option; for (option of Object.keys(options)) {
if (Array.isArray(options[option])) {
let s = '';
let data;
for (data of options[option]) {
s += data;
s += options[option].indexOf(data) < (options[option].length - 1) ? ', ' : '';
}
foptions[option] = s;
}
}
}
let am;
if (!dmch) {dmch = message.channel;}
if (!options.characters) {options.characters = [];}
if (options.gender.toLowerCase() === 'm') {options.gender = 'Male';}
if (options.gender.toLowerCase() === 'f') {options.gender = 'Female';}
let aniData = await AniData.findOne({id: options.anime});
if (!aniData && !forceAni) {return dmch.send(":thinking: hmmm... something went wrong there. I couldn't find the anime you specified. Please contact my dev if the problem persists.");}
let amEmbed = new Discord.MessageEmbed()
.setTitle(`New Character -> ${options.name}`)
.setDescription(`${queue ? 'Requested' : 'Added'} by ${message.author.tag}`)
.addField('Info', `**Name:** ${options.name}`)
.addField('Other', `**Anime**: ${forceAni ? options.anime : `${aniData.name} | ${aniData.japname} | \`${aniData.id}\``}\n\n**Gender**: ${options.gender}\n`)
.setColor("c375f0")
.setImage(options.thumbnail)
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
const addChar = async () => {
while (true) {options.id = require('../../util/makeid')(4); if (!await Char.findOne({id: options.id})) {break;}}
if (!queue) {
amEmbed.addField("Reviewed", `Reviewed and submitted by <@${message.author.id}>`);
client.misc.cache.chars.set(options.name, options.id);
client.misc.cache.charsID.set(options.id, options.name);
if (options.nicknames) {options.nicknames.forEach(nn => client.misc.cache.chars.set(nn, options.id));}
}
else {amEmbed.addField("ID", options.id);}
amEmbed.setAuthor({name: !queue ? "Character Added" : "Character Submitted", iconURL: message.author.displayAvatarURL()});
if (!queue) {options.queued = false;}
await new Char(options).save();
if (aniData) {
aniData.characters.push(options.id);
aniData.markModified('characters');
aniData.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177797705781308').send({embeds: [amEmbed]}).catch(() => {})).catch(() => {});
if (options.images && options.images.length) {
let imagesEmbed = new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Image${client.utils.s(options.images.length)} ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${options.name}** | \`${options.id}\` from ${client.misc.cache.animeID.get(options.anime)}`)
.setThumbnail(options.thumbnail)
.setImage(options.images[0])
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
if (options.images.length > 1) {imagesEmbed.addField("Images", options.images.join("\n"));}
client.guilds.cache.get('762707532417335296').channels.cache.get('932177850239422494').send({
embeds: [imagesEmbed], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {});
}
if (am) {am.delete().catch(() => {});}
return dmch.send(`Your character${options.images.length ? `, and ${options.gender === 'Male' ? 'his' : options.gender === 'Female' ? 'her' : 'their'} ${options.images.length} ${client.utils.as(options.images.length, 'image')},` : ''} has been ${!queue ? "added" : "submitted"}`);
};
if (!options.force) {
try {
am = await dmch.send({embeds: [amEmbed]});
await am.react('👍').catch(() => {});
await am.react('👎').catch(() => {});
} catch {return dmch.send(":thinking: hmmm... something went wrong there. I might not have permissions to add reactions to messages, and this could be the issue.");}
try {
let rc = am.createReactionCollector({filter: (r, u) => ['👍', '👎'].includes(r.emoji.name) && u.id === message.author.id, max: 1, time: 60000});
rc.on("collect", async r => {
if (r.emoji.name !== '👎') {
await addChar();
} else {
return dmch.send("Oh, okay. I'll discard that then!");
}
});
rc.on("end", collected => {if (!collected.size) {return message.author.send("Looks like you ran out of time! Try again?");}});
} catch {return message.author.send("Hmm... there was some kind of error when I tried to submit that character. Try again, and if it keeps not working, then go yell at my devs!");}
} else {await addChar();}
return;
}
if (['e', 'edit'].includes(args[0].toLowerCase())) {
args.shift();
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {return await message.channel.send("Since you aren't a Natsuki Staff member, you can't edit characters.");}
if (!args.length) {return message.channel.send("You have to provide tags to edit characters with.");}
options = new TagFilter([
new Tag(['name', 'n'], 'name', 'append'),
new Tag(['rn', 'rename'], 'rename', 'append'),
new Tag(['description', 'desc', 'p', 'personality'], 'personality', 'append'),
//new Tag(['anime', 'ani', 'a'], 'anime', 'append'),
new Tag(['thumb', 'thumbnail', 't'], 'thumbnail', 'append'),
new Tag(['img', 'image', 'i'], 'images', 'listAppend'),
new Tag(['loveInterest', 'dating', 'married', 'li'], 'loveInterest', 'append'),
new Tag(['gender', 'g', 'sex'], 'gender', 'append'),
new Tag(['nickname', 'nn', 'nick'], 'nicknames', 'listAppend'),
new Tag(['force', 'f'], 'force', 'toggle')
]).test(args.join(' '));
if (!options.name) {
let tempani = await ask(message, "What character would you like to edit?", 60000, false, true);
if (!tempani) {return;}
options.name = tempani.split(/\s+/g);
}
let asr = await chs(message, client, options.name.toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {fn = asr.id;}
let ech = await Char.findOne({id: fn});
if (!ech) {return message.channel.send("\\*Head scratching* that character seems to have vanished into thin air. Try again or go yell at my devs.");}
if (message.attachments.size) {
if (options.thumbnail) {
if (!options.images) {options.images = [];}
Array.from(message.attachments.keys()).forEach(i => options.images.push(message.attachments.get(i).url));
} else {
if (!options.images) {options.images = [];}
let att = Array.from(message.attachments.keys());
if (message.attachments.size > 1) {
for (let i = 1; i < att.length; i++) {
options.images.push(message.attachments.get(att[i]).url);
}
}
options.thumbnail = message.attachments.get(att[0]).url;
}
}
Object.keys(options).forEach(async o => {if (![undefined, null].includes(options[o]) && o !== 'name') {
/*if (o === 'anime') {
let ani = await AniData.findOne({id: ech.anime});
if (ani) {
}
}*/
if (Array.isArray(options[o])) {options[o].forEach(i => ech[o].push(i));}
else {ech[o] = options[o];}
ech.markModified(o);
if (o === 'rename') {
delete ech.rename;
client.misc.cache.charsID.set(ech.id, options.rename);
client.misc.cache.chars.delete(ech.name);
client.misc.cache.chars.set(options.rename, ech.id);
ech.name = options.rename;
}
}});
ech.save();
return message.channel.send('Character updated.');
}
if (['s', 'search'].includes(args[0].trim().toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to search for?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -100000);
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
} else {
await message.channel.send({embeds: [asr.embed]});
}
return;
}
if (['v', 'view'].includes(args[0].trim().toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to view?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700);
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
} else {
await message.channel.send({embeds: [asr.embed]});
}
return;
}
if (['reject'].includes(args[0].trim().toLowerCase())) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {await message.channel.send("Since you aren't a Natsuki Staff member, you can't reject character submissions!");}
let tr = await Char.findOne({id: args[1].toLowerCase()});
if (!tr) {return message.reply("That character submission doesn't seem to exist!");}
if (tr.queued !== true) {return message.reply("That character was already accepted, so you can't reject it.");}
return await CharData.deleteOne({id: args[1].toLowerCase()})
.then(() => {return message.channel.send("I got that submission out of here!");})
.catch(() => {return message.reply("It seems that submission wasn't deleted for some reason. \*insert head scratching*");});
}
if (['accept', 'approve'].includes(args[0].toLowerCase())) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {await message.channel.send("Since you aren't a Natsuki Staff member, you can't accept character submissions!");}
let tr = await Char.findOne({id: args[1].toLowerCase()});
if (!tr) {return message.reply("That character submission doesn't seem to exist!");}
if (tr.queued !== true) {return message.reply("That character was already accepted, so you can't accept it again...");}
tr.queued = false;
return await tr.save()
.then(async () => {
client.misc.cache.chars.set(tr.name.normalize("NFD").replace(/[\u0300-\u036f]/g, ""), tr.id);
tr.nicknames.forEach(nn => client.misc.cache.chars.set(nn.normalize("NFD").replace(/[\u0300-\u036f]/g, ""), tr.id));
client.misc.cache.charsID.set(tr.id, tr.name.normalize("NFD").replace(/[\u0300-\u036f]/g, ""));
client.misc.cache.charsNum++;
client.misc.cache.charsLove.set(char.id, char.loved);
client.guilds.cache.get('762707532417335296').channels.cache.get('932177797705781308').send({embeds: [new Discord.MessageEmbed()
.setTitle(`Character Accepted -> ${tr.name}`)
.setAuthor({name: 'Character Approved', iconURL: message.author.displayAvatarURL()})
.setThumbnail(tr.thumbnail)
.setDescription(`${tr.name} has been approved, and is now available to all Natsuki users.`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]}).catch(() => {});
let ani = await AniData.findOne({id: tr.anime});
if (!ani) {
tr.queued = false;
tr.save();
return message.channel.send("I can't accept that submission for you because it doesn't have a valid anime listed. Please report this to a dev.");
}
ani.characters.push(tr.id);
ani.markModified('characters');
ani.save();
return message.channel.send("I've accepted that submission.");
})
.catch(() => {return message.reply("It seems that submission wasn't accepted for some reason. \*insert head scratching*");});
}
if (['r', 'rand', 'random', 'any'].includes(args[0].toLowerCase())) {
let asr = await chs(message, client, client.misc.cache.chars.random(), -100000);
return await message.channel.send({embeds: [asr.embed]});
}
if (['l', 'love', 'favorite', 'fav'].includes(args[0].toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to add to your favorites list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the character you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the character you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
let cf = await CF.findOne({uid: message.author.id}) || new CF({uid: message.author.id});
if (cf.loved.includes(fn)) {return message.channel.send("Looks like that character is already on your loved list!");}
let tfc = await Char.findOne({id: fn});
tfc.loved += 1;
tfc.markModified('loved');
tfc.save();
cf.loved.push(fn);
cf.markModified('loved');
cf.save();
return message.channel.send(`I've added **${tfc.name}** to your loved/favorited character list!`);
}
if (['loved', 'favorites', 'favs', 'ls', 'fs'].includes(args[0].toLowerCase())) {
let cf = await CF.findOne({uid: mention ? mention.id : message.author.id});
if (!cf || !cf.loved.length) {return message.channel.send(`Looks like ${mention ? 'they' : 'you'} haven't favorited any characters!`);}
let chars = cf.loved;
chars = chars.map(tc => client.misc.cache.charsID.get(tc));
let n = mention ? message.guild ? message.mentions.members.first().displayName : message.mentions.users.first().username : message.guild ? message.member.displayName : message.author.username;
return message.channel.send({embeds: [
new Discord.MessageEmbed()
.setAuthor({name: `${n}${n.endsWith('s') ? '' : "'s"} Favorited Characters`, iconURL: mention ? mention.displayAvatarURL() : message.author.displayAvatarURL()})
.setDescription(`**${chars.length} character${chars.length === 1 ? '': 's'} favorited**\n\n${chars.join(", ")}`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
}
if (['i', 'im', 'img', 'image'].includes(args[0].toLowerCase())) {
args.shift();
let list = false;
if (args[0] && ['l', 'list'].includes(args[0].toLowerCase())) {
list = true;
args.shift();
}
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to add to add an image to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
let collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This image will be __submitted__ for reviewal.");
queue = true;
}
let ch = await Char.findOne({id: fn});
if (list) {
ch.images.push(ch.thumbnail);
let pages = ch.images.map(im => new Discord.MessageEmbed()
.setTitle(ch.name)
.setDescription(`**Name:** ${ch.name} -> ${ch.images.length} ${client.utils.as(ch.images.length, 'image')}`)
.addField('Other', `**Anime**: ${client.misc.cache.animeID.get(ch.anime)}\n\n**Gender**: ${ch.gender}\n`)
.setColor("c375f0")
.setImage(im)
);
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send({embeds: [pages[0].setTimestamp()]});}
} else {
args.shift();
let images = [];
if (message.attachments.size > 1) {
Array.from(message.attachments.keys()).forEach(i => images.push(message.attachments.get(i).url));
} else {
let tempchar = message.attachments.size
? message.attachments.first().url
: await ask(message, `Please paste the image or a link to the image you'd like to add to **${ch.name}**.`, 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
images.push(args.join(" "));
}
let f;
images.forEach(img => {if (!img.match(/^https:\/\/(?:[\w\-].?)+[\/\w\-%()_]+\.(?:png|jpg|jpeg|gif|webp)$/gm)) {f = true; return message.channel.send("I don't think that's an image. Try again?");}})
if (f) {return;}
if (images.length === 1) {
let img = images[0];
if (!queue) {
ch.images.push(img);
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Image ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\` from ${client.misc.cache.animeID.get(ch.anime)}`)
.setThumbnail(ch.thumbnail)
.setImage(img)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Character image ${queue ? "submitted" : "added"} to **${ch.name}**.`);
} else {
if (!queue) {
images.forEach(img => ch.images.push(img));
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Images ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\` from ${client.misc.cache.animeID.get(ch.anime)}`)
.addField("Images", images.map(img => `${img}\n`).join(""))
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Character images (${images.length}) ${queue ? "submitted" : "added"} to **${ch.name}**.`);
}
}
}
if (['la', 'listani', 'listanime', 'inani', 'inanime', 'ia'].includes(args[0].toLowerCase())) {
args.shift();
let paginate = args[0] && args[0].toLowerCase() === 'paginate';
if (paginate) {args.shift();}
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to view the characters of?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").toLowerCase(), undefined, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
let collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {fn = asr.id;}
let anime = await AniData.findOne({id: fn});
if (!anime) {return message.channel.send("Huh... even though I knew exactly what anime you were looking for, I scoured my endless tomes of knowledge and couldn't find it. I would definitely talk to my devs.");}
if (paginate) {
let chs = [];
anime.characters.forEach(ch => chs.push(Char.findOne({id: ch})));
chs = await Promise.all(chs);
let pages = [];
chs.forEach(ch => pages.push( new Discord.MessageEmbed()
.setTitle(ch.name)
.setDescription(`**Name:** ${ch.name}`)
.addField('Other', `**Anime**: ${`${anime.name} | ${anime.japname} | \`${anime.id}\``}\n\n**Gender**: ${ch.gender}\n`)
.addField("Loved by", `**${ch.loved}** Natsuki user${ch.loved === 1 ? '' : 's'}!\n\`${prefix}char love ${ch.name}\``)
.setColor("c375f0")
.setImage(ch.thumbnail)
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
));
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, endTime: 60000});
} else {
return message.channel.send({embeds: [
new Discord.MessageEmbed()
.setTitle(`${anime.name}: Characters`)
.setThumbnail(anime.thumbnail)
.setDescription(`**${anime.characters.length} Characters**\n` + anime.characters.map(ch => client.misc.cache.charsID.get(ch)).join(", "))
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
.setColor('c375f0')
]});
}
}
if (['nn', 'nickname', 'altname', 'nick'].includes(args[0].toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to add to add a nickname to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This nickname will be __submitted__ for reviewal.");
queue = true;
}
let ch = await Char.findOne({id: fn});
let nn = await ask(message, `What nickname would you like to add to ${ch.name}?`, 60000, false, true);
if (!nn) {return;}
if (ch.nicknames.map(nickn => nickn.toLowerCase()).includes(nn.toLowerCase())) {return message.channel.send(`Looks like **${ch.name}** already has the nickname "${nn}".`);}
if (!queue) {
ch.nicknames.push(nn);
ch.markModified('nicknames');
ch.save();
client.misc.cache.chars.set(nn, ch.id);
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177814638186516').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Character Nickname ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\` from ${client.misc.cache.animeID.get(ch.anime)}`)
.addField("Name", nn)
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Character nickname ${queue ? "submitted" : "added"}.`);
}
return message.channel.send(`Invalid arg! Syntax: \`${prefix}char <add|view|search|random|love|loved>\``);
}
};

@ -1,136 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const Char = require('../../models/char');
const ask = require('../../util/ask');
const chs = require('../../util/anime/charsearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "charimage",
aliases: ['ci', 'characterimage', 'charimg'],
meta: {
category: 'Anime',
description: "Add an image to a character",
syntax: '`charimage <character> <imageURL|attachedImage>` or `charimage <list> <character>`',
extra: null,
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Character Images")
.setDescription("Add an image to a character, or list the characters' images.")
.addField("Notice", "If you're not a member of Natsuki staff, this image will be submitted for reviewal.")
.addField("Syntax", "`charimage <character> <imageURL|attachedImage>` or `charimage <list> <character>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
let list = false;
if (args[0] && ['l', 'list'].includes(args[0].toLowerCase())) {
list = true;
args.shift();
}
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to add to add an image to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
let collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This image will be __submitted__ for reviewal.");
queue = true;
}
let ch = await Char.findOne({id: fn});
if (list) {
ch.images.push(ch.thumbnail);
let pages = ch.images.map(im => new Discord.MessageEmbed()
.setTitle(ch.name)
.setDescription(`**Name:** ${ch.name} -> ${ch.images.length} ${client.utils.as(ch.images.length, 'image')}`)
.addField('Other', `**Anime**: ${client.misc.cache.animeID.get(ch.anime)}\n\n**Gender**: ${ch.gender}\n`)
.setColor("c375f0")
.setImage(im)
);
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send({embeds: [pages[0].setTimestamp()]});}
} else {
args.shift();
let images = [];
if (message.attachments.size > 1) {
Array.from(message.attachments.keys()).forEach(i => images.push(message.attachments.get(i).url));
} else {
let tempchar = message.attachments.size
? message.attachments.first().url
: await ask(message, `Please paste the image or a link to the image you'd like to add to **${ch.name}**.`, 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
images.push(args.join(" "));
}
let f;
images.forEach(img => {if (!img.match(/^https:\/\/(?:[\w\-].?)+[\/\w\-%()_]+\.(?:png|jpg|jpeg|gif|webp)$/gm)) {f = true; return message.channel.send("I don't think that's an image. Try again?");}})
if (f) {return;}
if (images.length === 1) {
let img = images[0];
if (!queue) {
ch.images.push(img);
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Image ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\` from ${client.misc.cache.animeID.get(ch.anime)}`)
.setThumbnail(ch.thumbnail)
.setImage(img)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Character image ${queue ? "submitted" : "added"} to **${ch.name}**.`);
} else {
if (!queue) {
images.forEach(img => ch.images.push(img));
ch.markModified('images');
ch.save();
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177850239422494').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Images ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\` from ${client.misc.cache.animeID.get(ch.anime)}`)
.addField("Images", images.map(img => `${img}\n`).join(""))
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Character images (${images.length}) ${queue ? "submitted" : "added"} to **${ch.name}**.`);
}
}
}
};

@ -1,29 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "charlb",
aliases: ['chlb', 'charleaderboard', 'characterlb', 'characterleaderboard'],
meta: {
category: 'Anime',
description: "Find out the most loved characters across Natsuki's users!",
syntax: '`charlb`',
extra: null
},
help: "This command has no special syntax!",
async execute(message, msg, args, cmd, prefix, mention, client) {
return message.channel.send({embeds: [
new Discord.MessageEmbed()
.setTitle("Character Love Leaderboard")
.setDescription(
Array.from(client.misc.cache.charsLove.keys())
.sort((a, b) => client.misc.cache.charsLove.get(a) - client.misc.cache.charsLove.get(b))
.reverse()
.slice(0, 10)
.map((c, i) => `${i+1}. **${client.misc.cache.charsLove.get(c)} vote${client.misc.cache.charsLove.get(c) === 1 ? '' : 's'}** -> ${client.misc.cache.charsID.get(c)}`)
.join('\n')
).setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
};

@ -1,79 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const Char = require('../../models/char');
const ask = require('../../util/ask');
const chs = require('../../util/anime/charsearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "charnick",
aliases: ['chn', 'charnn', 'cnn'],
meta: {
category: 'Anime',
description: "Add a nickname to a character",
syntax: '`charnick <charName>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Char Nickname Adding")
.setDescription("Add a nickname to a character with this command. You will need to specify the name of the character first, then send the message, and after that, Natsuki will ask you for the nickname to add.")
.addField("Notice", "This nickname will be submitted unless you are a member of Natsuki staff.")
.addField("Syntax", "`charnick <charName>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to add to add a nickname to?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {fn = asr.id;}
let tu = await UserData.findOne({uid: message.author.id});
let queue = false;
if (!tu || !tu.staff) {
message.channel.send("This nickname will be __submitted__ for reviewal.");
queue = true;
}
let ch = await Char.findOne({id: fn});
let nn = await ask(message, `What nickname would you like to add to ${ch.name}?`, 60000, false, true);
if (!nn) {return;}
if (ch.nicknames.map(nickn => nickn.toLowerCase()).includes(nn.toLowerCase())) {return message.channel.send(`Looks like **${ch.name}** already has the nickname "${nn}".`);}
if (!queue) {
ch.nicknames.push(nn);
ch.markModified('nicknames');
ch.save();
client.misc.cache.chars.set(nn, ch.id);
}
client.guilds.fetch('762707532417335296').then(g => g.channels.cache.get('932177814638186516').send({
embeds: [
new Discord.MessageEmbed()
.setAuthor({name: message.author.username, iconURL: message.author.displayAvatarURL()})
.setTitle(`New Character Nickname ${queue ? "Submitted" : "Added"}`)
.setDescription(`For **${ch.name}** | \`${ch.id}\` from ${client.misc.cache.animeID.get(ch.anime)}`)
.addField("Name", nn)
.setThumbnail(ch.thumbnail)
.setColor('c375f0')
.setTimestamp()
.setFooter({text: "Natsuki"})
], content: queue ? '<@330547934951112705>' : undefined
}).catch(() => {})).catch(() => {});
return message.channel.send(`Character nickname ${queue ? "submitted" : "added"}.`);
}
};

@ -1,57 +0,0 @@
const AF = require("../../models/anifav");
const AniData = require('../../models/anime');
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "haswatched",
aliases: ['hw', 'chwatch', 'chwa'],
meta: {
category: 'Anime',
description: "Check if you or another user has watched an anime",
syntax: '`haswatched [@user|userID] <animeName>`',
extra: null
},
cooldown: {
time: 10000,
silent: false
},
help: "Check if you or another user has watched an anime with `{{p}}haswatched`",
async execute(message, msg, args, cmd, prefix, mention, client) {
let user;
if (args[0] && args[0].match(/<@!?\d+>|\d+/gm)) {
user = message.guild ? (mention || client.users.cache.get(args[0])) : message.author;
if (!user) {return message.channel.send("Hmmm... for some reason I can't find the user you're looking for?");}
args.shift();
} else {user = message.author;}
if (!args[0]) {
let tempchar = await ask(message, `What anime would you like to check if ${user.id === message.author.id ? "you" : "they"}'ve watched?`, 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {fn = asr.id;}
let ani = await AniData.findOne({id: fn});
let af = await AF.findOne({uid: user.id});
if (!af || !af.watched.includes(fn)) {return message.channel.send(`${user.id === message.author.id ? "You haven't" : "That person hasn't"} watched **${ani.name}**${af && af.toWatch.includes(fn) ? `, *but* it is on ${user.id === message.author.id ? "your" : "their"} watch list` : ''}.`);}
else {return message.channel.send(`${user.id === message.author.id ? "You have" : "That person has"} watched **${ani.name}**.`);}
}
};

@ -1,60 +0,0 @@
const UserData = require('../../models/user');
const CharData = require('../../models/char');
const ask = require('../../util/ask');
const chs = require('../../util/anime/charsearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "highvalue",
aliases: ['hvc', 'mainchar', 'maincharacter'],
meta: {
category: 'Anime',
description: "Mark a character as high value, making their rarity stats different",
syntax: '`highvalue <char>`',
extra: null
},
help: "Mark a character as high value. Must be a member of Natsuki staff to do this.",
async execute(message, msg, args, cmd, prefix, mention, client) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {return message.channel.send("You cannot do this unless you are a member of Natsuki staff.");}
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to add to your favorites list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the character you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the character you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
const char = await CharData.findOne({id: fn});
if (!char) {return message.channel.send("For some reason, I couldn't find that character. This specific error message should be reported to my devs!");}
char.highValue = true;
char.save();
return message.channel.send(`I have made **${char.name}** a High-Value Character`);
}
};

@ -1,53 +0,0 @@
const Discord = require('discord.js');
const fz = require('fuzzysort');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "listanime",
aliases: ['la', 'listani', 'lanime', 'allani', 'allanime', 'anilist', 'animelist', 'anil'],
meta: {
category: 'Anime',
description: "View a list of every anime Natsuki has in her database at the moment",
syntax: '`listanime [query]`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Anime Listing")
.setDescription("See a list of every Natsuki anime, or add a search query.")
.addField("Syntax", "`listanime [query]`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
let anime = [];
let matches = `anime in the database`;
if (args.length) {
anime = fz.go(args.join(" "), Array.from(client.misc.cache.animeID.values()), {limit: 200}).sort((a,b)=>a.score-b.score).map(k => client.misc.cache.anime.get(k.target));
if (!anime.length) {return message.channel.send("That query returned no results!");}
matches = `matches`;
} else {anime = Array.from(client.misc.cache.animeID.keys());}
let pages = [];
let pl = (Math.floor(anime.length / 10) + 1);
for (let i = 0; i < pl; i++) {
if (!anime[(i * 10) + 1]) {break;}
let s = `${anime.length} ${matches}.\n\n`;
let ps = [];
for (let x = 0; x < 10; x++) {
if (!anime[(i * 10) + x]) {break;}
ps.push(`**${x + (i * 10) + 1}.** ${client.misc.cache.animeID.get(anime[(i * 10) + x])}`);
}
s = `${s}${ps.join('\n')}`;
pages.push(new Discord.MessageEmbed()
.setTitle("Anime Database")
.setThumbnail(client.user.displayAvatarURL({size: 1024, dynamic: true, format: 'png'}))
.setDescription(s)
.setColor('c375f0')
.setTimestamp()
);
}
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send({embeds: [pages[0].setFooter({text: "Natsuki"})]});}
}
};

@ -1,83 +0,0 @@
const Discord = require('discord.js');
const AF = require("../../models/anifav");
const AniData = require('../../models/anime');
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "loveani",
aliases: ['lani', 'loveanime', 'favani', 'favoriteani', 'favoriteanime', 'fa', 'favani'],
meta: {
category: 'Anime',
description: "Show your love for an anime",
syntax: '`loveani <charName|view>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Character Favoriting")
.setDescription("Show your love for a character with this command. A replica of running `char love <charName>`.")
.addField("Syntax", "`lovechar <charName|view>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (args[0] && ['v', 'view'].includes(args[0].toLowerCase())) {
let cf = await AF.findOne({uid: mention ? mention.id : message.author.id});
if (!cf || !cf.liked.length) {return message.channel.send(`Looks like ${mention ? 'they' : 'you'} haven't favorited any anime!`);}
let chars = cf.liked;
chars = chars.map(tc => client.misc.cache.animeID.get(tc));
let n = mention ? message.guild ? message.mentions.members.first().displayName : message.mentions.users.first().username : message.guild ? message.member.displayName : message.author.username;
return message.channel.send({embeds: [
new Discord.MessageEmbed()
.setAuthor({name: `${n}${n.endsWith('s') ? '' : "'s"} Favorited Anime`, iconURL: mention ? mention.displayAvatarURL() : message.author.displayAvatarURL()})
.setDescription(`**${chars.length} anime favorited**\n\n${chars.join(", ")}`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
} else {
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to your favorites list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the anime you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the anime you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
let cf = await AF.findOne({uid: message.author.id}) || new AF({uid: message.author.id});
if (cf.liked.includes(fn)) {return message.channel.send("Looks like that anime is already on your loved list!");}
let tfc = await AniData.findOne({id: fn});
tfc.liked += 1;
tfc.save();
cf.liked.push(fn);
cf.markModified('liked');
cf.save();
return message.channel.send(`I've added **${tfc.name}** to your loved/favorited anime list!`);
}
}
};

@ -1,84 +0,0 @@
const Discord = require('discord.js');
const CF = require("../../models/charfav");
const Char = require('../../models/char');
const ask = require('../../util/ask');
const chs = require('../../util/anime/charsearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "lovechar",
aliases: ['lc', 'lovecharacter', 'lchar', 'lch', 'favchar', 'favoritechar', 'favoritecharacter', 'fch', 'favch'],
meta: {
category: 'Anime',
description: "Show your love for an anime character",
syntax: '`lovechar <charName|view>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Character Favoriting")
.setDescription("Show your love for a character with this command. A replica of running `char love <charName>`.")
.addField("Syntax", "`lovechar <charName|view>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (args[0] && ['v', 'view'].includes(args[0].toLowerCase())) {
let cf = await CF.findOne({uid: mention ? mention.id : message.author.id});
if (!cf || !cf.loved.length) {return message.channel.send(`Looks like ${mention ? 'they' : 'you'} haven't favorited any characters!`);}
let chars = cf.loved;
chars = chars.map(tc => client.misc.cache.charsID.get(tc));
let n = mention ? message.guild ? message.mentions.members.first().displayName : message.mentions.users.first().username : message.guild ? message.member.displayName : message.author.username;
return message.channel.send({embeds: [
new Discord.MessageEmbed()
.setAuthor({name: `${n}${n.endsWith('s') ? '' : "'s"} Favorited Characters`, iconURL: mention ? mention.displayAvatarURL() : message.author.displayAvatarURL()})
.setDescription(`**${chars.length} character${chars.length === 1 ? '': 's'} favorited**\n\n${chars.join(", ")}`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
} else {
if (!args[0]) {
let tempchar = await ask(message, "What character would you like to add to your favorites list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await chs(message, client, args.join(" ").trim().toLowerCase(), -700);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the character you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the character you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.chars.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the character you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the character you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the character you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
let cf = await CF.findOne({uid: message.author.id}) || new CF({uid: message.author.id});
if (cf.loved.includes(fn)) {return message.channel.send("Looks like that character is already on your loved list!");}
let tfc = await Char.findOne({id: fn});
tfc.loved += 1;
tfc.markModified('loved');
tfc.save();
cf.loved.push(fn);
cf.markModified('loved');
cf.save();
return message.channel.send(`I've added **${tfc.name}** to your loved/favorited character list!`);
}
}
};

@ -1,103 +0,0 @@
const Discord = require('discord.js');
const AF = require("../../models/anifav");
const AniData = require('../../models/anime');
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "watched",
aliases: ['watchedani', 'fl', 'finishedani', 'finshedlist'],
meta: {
category: 'Anime',
description: "Add an anime to your list of finished anime",
syntax: '`watched <anime>`',
extra: null
},
help: "Add an anime to your list of finished animes with {{p}}watched.",
async execute(message, msg, args, cmd, prefix, mention, client) {
if (args[0] && ['v', 'view'].includes(args[0].toLowerCase())) {
if (mention) {args.shift();}
let user = mention || message.author;
let af = await AF.findOne({uid: user.id});
if (!af || !af.watched.length) {return message.channel.send(`${user.id === message.author.id ? "You haven't" : "That person hasn't"} watched any anime.`);}
let pages = [];
let pl = (Math.floor(af.watched.length / 10) + 1);
for (let i = 0; i < pl; i++) {
let s = '';
for (let x = 0; x < 10; x++) {
if (!af.watched[(i * 10) + x]) {break;}
s += `**${x + (i * 10) + 1}.** ${client.misc.cache.animeID.get(af.watched[(i * 10) + x])}\n`;
}
pages.push(new Discord.MessageEmbed()
.setAuthor({
name: message.guild ? message.guild.members.cache.get(user.id).displayName : user.username,
iconURL: message.guild ? message.guild.members.cache.get(user.id).displayAvatarURL({dynamic: true}) : user.displayAvatarURL({dynamic: true})
})
.setTitle("Finished Anime List")
.setDescription(s)
.setColor('c375f0')
.setTimestamp()
);
}
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send({embeds: [pages[0].setFooter({text: "Natsuki"})]});}
}
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to your finished list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the anime you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the anime you're looking for then I would try again with a more narrow search.");}
fn = asr.id;
}
let af = await AF.findOne({uid: message.author.id}) || new AF({uid: message.author.id});
if (af.watched.includes(fn)) {return message.channel.send("Looks like that anime is already on your finished list!");}
let tfc = await AniData.findOne({id: fn});
tfc.watchers += 1;
tfc.markModified('watchers');
af.watched.push(fn);
af.markModified('watched');
let dw = false;
if (af.toWatch.includes(fn)) {
af.toWatch.splice(af.toWatch.indexOf(fn), 1);
af.markModified('toWatch');
tfc.listed--;
tfc.save();
dw = true;
}
af.save();
tfc.save();
return message.channel.send(`I've added **${tfc.name}** to your list of finished animes!${dw ? " I've also removed it from your watch list for you :p" : ''}`);
}
};

@ -1,29 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "watchedlb",
aliases: ['wlb', 'watchleaderboard', 'watchedlb', 'finishedleaderboard', 'flb', 'finishlb', 'finishleaderboard'],
meta: {
category: 'Anime',
description: "Find out the most watched anime across Natsuki's users!",
syntax: '`watchedlb`',
extra: null
},
help: "This command has no special syntax!",
async execute(message, msg, args, cmd, prefix, mention, client) {
return message.channel.send({embeds: [
new Discord.MessageEmbed()
.setTitle("Anime Watch Count Leaderboard")
.setDescription(
Array.from(client.misc.cache.animeLove.keys())
.sort((a, b) => client.misc.cache.animeLove.get(a) - client.misc.cache.animeLove.get(b))
.reverse()
.slice(0, 10)
.map((c, i) => `${i+1}. **${client.misc.cache.animeLove.get(c)} watcher${client.misc.cache.animeLove.get(c) === 1 ? '' : 's'}** -> ${client.misc.cache.animeID.get(c)}`)
.join('\n')
).setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
};

@ -1,150 +0,0 @@
const Discord = require('discord.js');
const AF = require("../../models/anifav");
const AniData = require('../../models/anime');
const {Pagination} = require('../../util/pagination');
const ask = require('../../util/ask');
const ans = require('../../util/anime/anisearch');
module.exports = {
name: "watchlist",
aliases: ['wl'],
meta: {
category: 'Anime',
description: "Add anime to your list of animes you want to watch",
syntax: '`watchlist <view|add|remove>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> WatchList")
.setDescription("Add anime to your watchlist (not to be confused with \"watchedlist\"; this is your list of anime you want to watch but haven't yet. You can view your list or anothers' list as well.)")
.addField("Removal from List", "If you're removing an item from your list because you've *seen* the anime, please run the `watched` command instead. It will automatically remove the anime from your watchlist and place it in your watchedlist.")
.addField("Syntax", "`watchlist <view|add|remove>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}watchlist <view|add|remove>\``);}
if (['v', 'view'].includes(args[0].toLowerCase())) {
if (mention) {args.shift();}
let user = mention || message.author;
let af = await AF.findOne({uid: user.id});
if (!af || !af.toWatch.length) {return message.channel.send(`${user.id === message.author.id ? "You don't" : "That person doesn't"} have any watchlisted anime.`);}
let pages = [];
let pl = (Math.floor(af.toWatch.length / 10) + 1);
for (let i = 0; i < pl; i++) {
let s = '';
for (let x = 0; x < 10; x++) {
if (!af.toWatch[(i * 10) + x]) {break;}
s += `**${x + (i * 10) + 1}.** ${client.misc.cache.animeID.get(af.toWatch[(i * 10) + x])}\n`;
}
pages.push(new Discord.MessageEmbed()
.setAuthor({
name: message.guild ? message.guild.members.cache.get(user.id).displayName : user.username,
iconURL: message.guild ? message.guild.members.cache.get(user.id).displayAvatarURL({dynamic: true}) : user.displayAvatarURL({dynamic: true})
})
.setTitle("Anime Watch List")
.setDescription(s)
.setColor('c375f0')
.setTimestamp()
);
}
if (pages.length > 1) {
let pag = new Pagination(message.channel, pages, message, client, true);
return await pag.start({user: message.author.id, time: 60000});
} else {return message.channel.send({embeds: [pages[0].setFooter({text: "Natsuki"})]});}
}
if (['a', 'add'].includes(args[0].toLowerCase())) {
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to add to your watch list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
let noticeDel = await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
await asr.stop();
await asr.message.delete().catch(() => {});
await noticeDel.delete().catch(() => {});
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {
let preConfEmbed = await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the anime you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
preConfEmbed.delete().catch(() => {});
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the anime you're looking for then I would try again with a more narrow search.");}
fn = asr.id;}
let af = await AF.findOne({uid: message.author.id}) || new AF({uid: message.author.id});
if (af.toWatch.includes(fn)) {return message.channel.send("Looks like that anime is already on your watch list!");}
if (af.watched.includes(fn)) {return message.channel.send("That anime is on your **watched** list already...");}
const tfc = await AniData.findOne({id: fn});
if (!tfc) {return message.channel.send("Huh... guess that anime just... vanished into thin air? I would go yell at my devs.");}
af.toWatch.push(fn);
af.markModified('toWatch');
af.save();
tfc.listed++;
tfc.save();
return message.channel.send(`I've added **${client.misc.cache.animeID.get(fn)}** to your watch list! ${[`Let me know if it's any good when you get around to it :3`, `Hope it's good!`, 'Try not to wait *too* long before you watch it.', `I've heard good things about that one.`][Math.floor(Math.random() * 4)]}`);
}
if (['d', 'delete', 'remove', 'r'].includes(args[0].toLowerCase())) {
let conf = await ask(message, "Real quick, before anything else, I have to ask: are you trying to remove an anime from your watch list because you've now watched it?", 60000);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
if (conf) {return message.channel.send(`Good thing I asked then :brain: Please instead use the \`${prefix}watched\` command to handle removing the anime from your watch list.`);}
args.shift();
if (!args[0]) {
let tempchar = await ask(message, "What anime would you like to remove from your watch list?", 60000, false, true);
if (!tempchar) {return;}
args = tempchar.split(/\s+/g);
}
let asr = await ans(message, client, args.join(" ").trim().toLowerCase(), -700, 0);
let fn;
if (asr === 0) {
return message.channel.send("That search returned no results! Try again?");
} else if (asr instanceof Pagination) {
await asr.start({user: message.author.id, startPage: 0, endTime: 60000});
await asr.message.react('✅');
await message.channel.send("React with :white_check_mark: when you've found the anime you want!");
let arc;
try {arc = await asr.message.awaitReactions({filter: (r) => ['✅', '⏹'].includes(r.emoji.name), max: 1, errors: ['time']});}
catch {return message.reply("Looks like you didn't find the anime you were looking for.");}
collected = arc.first().emoji.name;
if (collected === '✅') {
fn = client.misc.cache.anime.get(asr.getCurrentPage().title.trim());
asr.stop();
}
else {return message.reply("Looks like you didn't find the anime you were looking for.");}
} else {
await message.channel.send({embeds: [asr.embed]});
let conf = await ask(message, "Is this the anime you meant?", 60000, undefined, undefined, true);
if (!['y', 'yes', 'ye', 'n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("You must specify yes or no! Please try again.");}
conf = ['y', 'yes', 'ye'].includes(conf.trim().toLowerCase());
if (!conf) {return message.channel.send("Well, I've got nothing, then. If that doesn't match the anime you're looking for then I would try again with a more narrow search.");}
fn = asr.id;}
let af = await AF.findOne({uid: message.author.id}) || new AF({uid: message.author.id});
if (!af.toWatch.includes(fn)) {return message.channel.send("Looks like that anime isn't on your watch list!");}
const tfc = await AniData.findOne({id: fn});
if (!tfc) {return message.channel.send("Huh... guess that anime just... vanished into thin air? I would go yell at my devs.");}
af.toWatch.splice(af.toWatch.indexOf(fn), 1);
af.markModified('toWatch');
af.save();
tfc.listed--;
tfc.save();
return message.channel.send(`${['Guess it wasn\'t worth the watch after all, huh?', 'Oof. Did you lose interest? Well, either way,', 'Got it, got it.', 'Okie dokie!'][Math.floor(Math.random() * 4)]} I've removed **${client.misc.cache.animeID.get(fn)}** from your watch list.`);
}
}
};

@ -1,42 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
module.exports = {
name: "admin",
help: new Discord.MessageEmbed()
.setTitle("Help -> Admin")
.setDescription("Make a user a Natsuki admin")
.addField("Syntax", "`admin <add|remove|check> <@user|userID>`")
.addField("Notice", "This command is only available to Natsuki developers."),
meta: {
category: 'Developer',
description: "Add or remove users as Natsuki admins",
syntax: '`admin <add|remove|check> <@user|userID>`',
extra: "You can check if a user is an admin without being a developer."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This is a guild-only command.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}\``);}
let person = mention ? mention : args[1] ? client.users.cache.has(args[1]) ? client.users.cache.get(args[1]) : null : null;
let tu = await UserData.findOne({uid: person ? person.id : message.author.id}) ? await UserData.findOne({uid: person ? person.id : message.author.id}) : new UserData({uid: person ? person.id : message.author.id});
if (['c', 'check'].includes(args[0])) {return message.reply(`${person ? person : message.member.displayName} ${tu.admin ? 'is' : 'is not'} a Natsuki Admin.`);}
if (!['a', 'add', 'r', 'remove'].includes(args[0])) {return message.reply("You must specify whether to `add` or `remove` someone as an admin.");}
if (!person) {return message.reply("You must mention someone to add as an admin, or use their ID.");}
let atu = await UserData.findOne({uid: message.author.id});
if ((!atu || !atu.developer) && !client.developers.includes(message.author.id)) {return message.reply('You must be a developer in order to add set admin statuses.');}
if (['a', 'add'].includes(args[0])) {tu.support = true; tu.staff = true; tu.admin = true;}
else {tu.admin = false; tu.developer = false;}
tu.save();
const logemb = (act) => new Discord.MessageEmbed()
.setAuthor({name: `Admin ${act}`, iconURL: message.author.displayAvatarURL()})
.setDescription("A user's Admin status was updated.")
.setThumbnail(person.displayAvatarURL({size: 1024}))
.addField("Name", person.username, true)
.addField("Developer", message.author.username, true)
.setColor("e8da3a")
.setFooter({text: "Natsuki"})
.setTimestamp();
client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915').send({embeds: [logemb(['a', 'add'].includes(args[0]) ? 'Added' : 'Removed')]});
return message.reply(`${message.guild.members.cache.get(person.id).displayName} is no${['a', 'add'].includes(args[0]) ? 'w' : ' longer'} an admin!`);
}
};

@ -1,83 +0,0 @@
const Discord = require('discord.js');
const AniData = require("../../models/anime");
const UserData = require('../../models/user');
const CharData = require('../../models/char');
const {Pagination} = require('../../util/pagination');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
module.exports = {
name: "anipulse",
aliases: ['ap'],
meta: {
category: 'Developer',
description: "Get a pulse check of anime",
syntax: '`anipulse`',
extra: null
},
help: "Not much to see here!",
async execute(message, msg, args, cmd, prefix, mention, client) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.staff) {return message.channel.send("You must be Natsuki staff in order to use this command.");}
let sm = await message.channel.send("One moment while I gather that information for you...");
let options = new TagFilter([
new Tag(['c', 'ch', 'char', 'chars', 'character', 'characters'], 'chars', 'append'),
new Tag(['a', 'ani', 'anime'], 'anime', 'append')
]).test(args.join(" "));
Object.keys(options).forEach(o => {if (isNaN(Number(options[o]))) {delete options[o];}});
let ani = client.misc.cache.animeID.size;
let char = client.misc.cache.charsID.size;
let nick = Array.from(client.misc.cache.chars.keys()).filter(x => !Array.from(client.misc.cache.charsID.values()).includes(x)).length;
let charThresh = (options.chars !== undefined ? Number(options.chars) : 2);
let aniThresh = (options.anime !== undefined ? Number(options.anime) : 10);
let lowChars = [];
for await (const ani of AniData.find()) {
if (ani.characters.length < aniThresh) {lowChars.push(ani);}
}
let lowIms = [];
for await (const char of CharData.find()) {
if (char.images.length < charThresh) {lowIms.push(char);}
}
let pages = [new Discord.MessageEmbed()
.setTitle("Anime Database Pulse Check")
.setDescription(`**${ani}** Anime\n**${char}** Characters\n**${nick}** Nicknames`)
.addField(`Anime with < ${aniThresh} character${client.utils.s(aniThresh)}`, `${lowChars.length}`)
.addField(`Characters with < ${charThresh} extra image${client.utils.s(charThresh)}`, `${lowIms.length}`)
.setColor('c375f0')
];
let ma = Math.floor(lowIms.length / 10);
for (let i = 0; i < ma; i++) {
if (!lowChars.slice(10 * i, Math.min((10 * i) + 10, lowChars.length)).length) {break;}
pages.push(new Discord.MessageEmbed()
.setTitle("Anime Database Pulse Check")
.setDescription(`**${ani}** Anime\n**${char}** Characters\n**${nick}** Nicknames`)
.addField(`Anime with < ${aniThresh} character${client.utils.s(aniThresh)}`, lowChars.slice(10 * i, Math.min((10 * i) + 10, lowChars.length)).filter(x=>![null,undefined].includes(x)).map((ani, y) => `${(10*i)+y+1}. ${ani.name} -> **${ani.characters.length}** Char${client.utils.s(ani.characters.length)}.`).join('\n'))
.setColor('c375f0')
);}
let mc = Math.floor(lowIms.length / 10);
for (let i = 0; i < mc; i++) {
if (!lowIms.slice(10 * i, Math.min((10 * i) + 10, lowIms.length)).length) {break;}
pages.push(new Discord.MessageEmbed()
.setTitle("Anime Database Pulse Check")
.setDescription(`**${ani}** Anime\n**${char}** Characters\n**${nick}** Nicknames`)
.addField(`Characters with < ${charThresh} extra image${client.utils.s(charThresh)}`, lowIms.slice(10 * i, Math.min((10 * i) + 10, lowIms.length)).filter(x=>![null,undefined].includes(x)).map((ch, y) => `${(10*i)+y+1}. ${ch.name} -> **${ch.images.length}** Image${client.utils.s(ch.images.length)}.`).join('\n'))
.setColor('c375f0')
);}
const pag = new Pagination(message.channel, pages, message, client, true);
sm.delete().catch(() => {});
return await pag.start({user: message.author.id, time: 60000});
}
};

@ -1,97 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const GuildData = require('../../models/guild')
module.exports = {
name: "blacklist",
aliases: ['bl'],
meta: {
category: 'Developer',
description: "Completely blocks a user or server from using Natsuki!",
syntax: '`blacklist <user|guild> <add|delete> [@mention|ID]`',
extra: null
},
help: "Disables a user from using Natsuki (Usage: `{{p}}blacklist <user|guild> <add|delete> [@mention|ID])`",
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send("Syntax: `blacklist <user|guild> <add|delete> [@mention|ID]`");}
let tu = await UserData.findOne({uid: message.author.id});
if (['g', 'guild'].includes(args[0].toLowerCase())) {
if (!tu || !tu.admin) {return message.channel.send('Sorry... you have to be a Natsuki Admin to do this!');}
let guild = !args[1].match(/\d+/) ? message.guild ? message.guild : null : client.guilds.cache.has(args[1]) ? client.guilds.cache.get(args[1]) : null;
if (!guild) {return message.channel.send("You must provide a guild ID or be in a guild that you wish to blacklist!");}
let tg = await GuildData.findOne({gid: guild.id}) || new GuildData({gid: guild.id});
if (args[1].match(/\d+/)) {args.shift();}
if (!args[1]) {return message.channel.send("You must specify whether to `add` or `del` a guild's blacklist!");}
if (message.guild.id === "762707532417335296") {return message.reply("You can't blacklist my support server!");}
if (['a', 'add'].includes(args[1].toLowerCase())) {
if (tg.blacklisted) {return message.reply("That guild is already blacklisted!");}
tg.blacklisted = true;
tg.save();
client.misc.cache.bl.guild.push(message.guild.id);
return message.channel.send("Gotcha! This server will not be able to use my commands!");
}
if (['r', 'rem', 'remove', 'd', 'del', 'delete'].includes(args[1].toLowerCase())) {
if (!tg.blacklisted) {return message.reply("That guild isn't blacklisted in the first place!");}
tg.blacklisted = false;
tg.save();
delete client.misc.cache.bl.guild[client.misc.cache.bl.guild.indexOf(message.guild.id)];
return message.channel.send("I have graced your merciful request; this server can once again make use of my wonderous abilities!");
}
return message.channel.send("Valid args: `[guildID] <add|del>`");
}
if (['u', 'user'].includes(args[0].toLowerCase())) {
args.shift();
if (!args[0]) {return message.channel.send("You must specify whether to `add` or `del` a user's blacklist!");}
function checkPerms(tu, bu) {
if (!tu.developer && bu.support) {message.channel.send("You can't blacklist any member of staff unless you're a developer!"); return null;}
if (!tu.admin) {message.channel.send("You must be at least admin to do that!"); return null;}
if (bu.developer) {message.channel.send("Developers cannot be blacklisted!"); return null;}
}
if (['a', 'add'].includes(args[0].toLowerCase())) {
let blacklistUser = args[1].match(/^<@!?\d+>$/) && mention && client.users.cache.has(mention.id) ? mention.id : client.users.cache.has(args[1]) ? client.users.cache.get(args[1]).id : null;
if (!blacklistUser) {return message.reply("You must specify a user to blacklist!");}
let usersData = await UserData.findOne( { uid: blacklistUser } ) || new UserData({uid: blacklistUser});
if (!checkPerms(tu, usersData)) {return;}
if (usersData.blacklisted === true) {return message.reply('they\'re already blacklisted :eyes:');}
await UserData.findOneAndUpdate({ uid: blacklistUser }, { blacklisted: true }.catch(() => {}));
client.misc.cache.bl.user.push(blacklistUser);
return message.channel.send(`Another one bites the dust! **${blacklistUser.user.tag}** has been blacklisted!`)
}
if (['r', 'rem', 'remove', 'd', 'del', 'delete'].includes(args[0].toLowerCase())) {
let blacklistedUser = args[1].match(/^<@!?\d+>$/) && mention && client.users.cache.has(mention.id) ? mention.id : client.users.cache.has(args[1]) ? client.users.cache.get(args[1]).id : null;
if (!blacklistedUser) { return message.reply("You need to specify who you're letting free..." );}
let userData = await UserData.findOne( { uid: blacklistedUser } ) || new UserData({uid: blacklistedUser});
if (!checkPerms(tu, userData)) {return;}
if(userData.blacklisted === false) {return message.reply('hate to break it you... they\'re not even blacklisted!');}
await UserData.findOneAndUpdate({ uid: blacklistedUser }, { blacklisted: false }.catch(() => {}));
delete client.misc.cache.bl.user[client.misc.cache.bl.user.indexOf(blacklistedUser)];
return message.channel.send(`Alright, there you go, I unblacklisted **${blacklistedUser.user.tag}**`)
}
return message.channel.send("Valid args: `<userID|@user> <add|del>`");
}
return message.channel.send("Valid args: `<user|guild>`");
}};

@ -1,23 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
module.exports = {
name: "cachesync",
aliases: ['preres', 'pres'],
meta: {
category: 'Developer',
description: "Sync cached XP, monners, and monitor data to make client safe for a restart",
syntax: '`cachesync`',
extra: null
},
help: "Nothing to see here!",
async execute(message, msg, args, cmd, prefix, mention, client) {
const tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.developer) && !client.developers.includes(message.author.id)) {return message.channel.send("You must be a developer in order to do this!");}
await require('../../util/lxp/cacheloop')(client);
await require('../../util/vcloop')(client);
await require('../../util/monitorloop')(client);
return message.channel.send("Cache synchronized with DB!");
}
};

@ -1,44 +0,0 @@
const Discord = require('discord.js');
const mongoose = require('mongoose');
const UserData = require('../../models/user');
module.exports = {
name: "developer",
aliases: ['dev'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Developer")
.setDescription("Add or remove users as Natsuki developers.")
.addField("Syntax", "`developer <add|remove> <@user|userID>`")
.addField("Notice", "You must already be a developer of Natsuki in order to use this command."),
meta: {
category: 'Developer',
description: "Add or remove users as Natsuki developers",
syntax: '`developer <add|remove|check> <@user|userID>`',
extra: "You can check if a user is a developer without being a developer."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This is a guild-only command!");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}developer <add|remove> <@user|userID>\``);}
let person = mention ? mention : args[1] ? client.users.cache.has(args[1]) ? client.users.cache.get(args[1]) : null : null;
let tu = await UserData.findOne({uid: person ? person.id : message.author.id}) ? await UserData.findOne({uid: person ? person.id : message.author.id}) : new UserData({uid: person ? person.id : message.author.id});
if (['c', 'check'].includes(args[0])) {return message.reply(`${person ? person : message.member.displayName} ${tu.developer ? 'is' : 'is not'} a Natsuki developer.`);}
if (!['a', 'add', 'r', 'remove'].includes(args[0])) {return message.reply("You must specify whether to `add` or `remove` someone as a developer.");}
if (!person) {return message.reply("You must mention someone to add as a developer, or use their ID.");}
let atu = await UserData.findOne({uid: message.author.id});
if ((!atu || !atu.developer) && !client.developers.includes(message.author.id)) {return message.reply('You must be a developer in order to add or remove someone else as a developer.');}
if (['a', 'add'].includes(args[0])) {tu.support = true; tu.staff = true; tu.admin = true; tu.developer = true;}
else {tu.developer = false;}
tu.save();
const logemb = (act) => new Discord.MessageEmbed()
.setAuthor({name: `Developer ${act}`, iconURL: message.author.displayAvatarURL()})
.setDescription("A user's Developer status was updated.")
.setThumbnail(person.displayAvatarURL({size: 1024}))
.addField("Name", person.username, true)
.addField("Developer", message.author.username, true)
.setColor("e8da3a")
.setFooter({text: "Natsuki"})
.setTimestamp();
client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915').send({embeds: [logemb(['a', 'add'].includes(args[0]) ? 'Added' : 'Removed')]});
return message.reply(`${message.guild.members.cache.get(person.id).displayName} is no${['a', 'add'].includes(args[0]) ? 'w' : ' longer'} a developer!`);
}
};

@ -1,52 +0,0 @@
const Discord = require('discord.js');
const chalk = require('chalk');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
module.exports = {
name: 'eval',
aliases: ['ev', ':'],
help: "Evaluates raw JavaScript code. *This is a __developer-only__ command.* Usage: `{{p}}eval <code>`",
meta: {
category: 'Developer',
description: "Evaluates raw JavaScript code. Nerd access only.",
syntax: '`eval <code>`',
extra: null
},
execute(message, msg, args, cmd, prefix, mention, client) {
try {
const dc = (...files) => {return files.forEach(file => {return delete require.cache[require.resolve(`../../${file}`)];});};
let timer = new Date().getTime();
if (!client.developers.includes(message.author.id)) {return message.channel.send("Sorry, but I've got trust issues, so only me devs can go commanding me around like that.");};
if (!args.length) return message.channel.send(`Syntax: \`${prefix}eval <code>\``);
let options = new TagFilter([new Tag(['s', 'silent', 'nr', 'noreply'], 'silent', 'toggle')]).test(args[0]);
if (options.silent) {args.shift();}
if (!args.length) {return message.channel.send("Silly goose, if you want me to do something, you have to tell me what!");}
const result = new Promise((resolve) => resolve(eval(args.join(' '))));
return result.then((output) => {
if (typeof output !== 'string') {
output = require('util').inspect(output, {depth: 0});
}
output = output.replace(client.config.token, 'Client Token')
.replace(client.config.database.password, 'Database Password')
.replace(client.config.database.cluster, 'Database Cluster');
return options.silent ? null : message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle('Client Evaluation')
.setDescription(`\`\`\`js\n${output}\n\`\`\``)
.setColor('c375f0')
.setFooter({text: `Natsuki | Evaluated in ${new Date().getTime() - timer}ms`, iconURL: client.user.displayAvatarURL()})
.setTimestamp()]});
}).catch(error => {return message.channel.send(`Error: \`${error}\`.`);});
} catch (error) {
let date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6);
console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to run n?eval`)}`, error);
return message.channel.send(`Error: \`${error}\`.`);
}
},
};

@ -1,39 +0,0 @@
const Discord = require('discord.js');
const cp = require("child_process");
module.exports = {
name: "execute",
aliases: ['exec'],
meta: {
category: 'Developer',
description: "Execute a console command",
syntax: '`Execute <command>`',
extra: null
},
help: "Dev only! Executes a console command",
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!client.developers.includes(message.author.id)) {return message.channel.send("You must be a developer to do this!");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}execute <command>\``);}
if (args.join(" ").match(/^rm\s+/gm)) {return message.channel.send(":(");}
return cp.exec(args.join(" "), function(error, stdout, stderr) {
if (error) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Error")
.setDescription(`\`\`\`${error}\`\`\``)
.setColor("ff446a")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()]}
);
}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Execution Successful")
.setDescription(`\`\`\`${stdout}\`\`\``)
.setColor("c375f0")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()]}
);
});
}
};

@ -1,20 +0,0 @@
const Discord = require('discord.js');
const ws = require('ws');
module.exports = {
name: "logger",
aliases: [],
meta: {
category: 'Developer',
description: "Websocket logs cause im cool",
syntax: '`nonya`',
extra: null
},
help: "Websocket logs cause im cool",
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!client.developers.includes(message.author.id)) {return message.channel.send("Fuck off");}
client.misc.loggers[args[0]] = new ws(`ws://${args[1]}:${args[2]}`);
return message.channel.send("Logger set");
}
};

@ -1,36 +0,0 @@
const Discord = require('discord.js');
const fs = require('fs');
const chalk = require('chalk');
const UserData = require('../../models/user');
const cp = require('child_process');
module.exports = {
name: "pull",
help: new Discord.MessageEmbed()
.setTitle("Help -> VCS Pull")
.setDescription("Pulls new commits from VCS")
.addField("Syntax", "`pull`")
.addField("Notice", "This command is only available to Natsuki developers."),
meta: {
category: 'Developer',
description: "Pull new commits from VSC and update the bot. Otaku zone, non-otakus not allowed.",
syntax: '`pull`',
extra: "You'll still need to use `reload` afterwards"
},
async execute(message, msg, args, cmd, prefix, mention, client) {
const tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.developer) {return message.channel.send("You must be a Natsuki developer in order to do this!");}
console.log(`\n${chalk.yellow('[WARN]')} >> ${chalk.gray('VCS:')} ${chalk.white('Initiating remote->local VCS sync/refresh!')}`);
cp.exec("git pull origin master", function(error, stdout, stderr) {
if (error) {
let date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6);
console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to pull from VCS`)}`, stderr || error);
} else {
console.log(`\n${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`VCS Pull successful`)}\n`);
}
return message.channel.send(`Done with ${error ? 'an error' : 'no errors'}!`);
});
}
};

@ -1,102 +0,0 @@
const Discord = require('discord.js');
const fs = require('fs');
const chalk = require('chalk');
const ora = require('ora');
const UserData = require("../../models/user");
module.exports = {
name: "reload",
aliases: ['relog', 'rel', 'refresh'],
help: new Discord.MessageEmbed()
.setTitle("Help -> System Reloading")
.setDescription("Reloads the system extensions by refreshing all command and event files into client without terminating the node process. *Hi I'm Wubzy and this makes no sense to anyone but discord.js devs because we're nerds*")
.addField("Syntax", "`refresh [log]`. Adding 'log' will log to the console as though the bot were in startup.")
.addField("Notice", "This command is only available to Natsuki developers."),
meta: {
category: 'Developer',
description: "Refresh all client commands and events and clear most of the require cache. Only two people can use this command and they're probably not you.",
syntax: '`reload`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
const tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.developer) {return message.channel.send("You must be a Natsuki developer in order to do this!");}
if (!args.length) {
let timer = new Date().getTime();
let commands = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
let dirSet = new Map();
fs.readdirSync('./commands').filter(file => !file.includes('.')).forEach(dir => fs.readdirSync(`./commands/${dir}`).filter(file => file.endsWith('.js')).forEach(x => {commands.push(x); dirSet.set(x, dir)}));
console.log(`\n${chalk.yellow('[WARN]')} >> ${chalk.gray('Reload:')} ${chalk.white('All commands and events are being reloaded!')}`);
console.log(`${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`Developer ${message.author.username} initiated the system refresh`)}\n`);
let cmdspinner = ora(chalk.blue('Loading Commands')).start();
['commands', 'aliases'].forEach(x => client[x] = new Discord.Collection());
for (let commandf of commands) {
if (Object.keys(require.cache).includes(require.resolve(`../../commands/${dirSet.has(commandf) ? `${dirSet.get(commandf)}/`: ''}${commandf}`))) {delete require.cache[require.resolve(`../../commands/${dirSet.has(commandf) ? `${dirSet.get(commandf)}/`: ''}${commandf}`)];}
let command = require(`../../commands/${dirSet.has(commandf) ? `${dirSet.get(commandf)}/`: ''}${commandf}`);
client.commands.set(command.name, command);
if (command.aliases) {command.aliases.forEach(a => client.aliases.set(a, command.name));}
}
cmdspinner.stop(); cmdspinner.clear();
console.log(`${chalk.gray('[PROC]')} >> ${chalk.blue('Loaded all Commands')}`);
let eventspinner = ora(chalk.blue('Loading Events')).start();
let eventFilter = fs.readdirSync('./events/').filter(x => x.endsWith('.js'));
for (let file of eventFilter) {
let evtName = file.split('.')[0];
if (Object.keys(require.cache).includes(require.resolve('../../events/' + file))) {delete require.cache[require.resolve('../../events/' + file)];}
let evt = require('../../events/' + file);
client.removeAllListeners(evtName);
client.on(evtName, evt.bind(null, client));
}
eventspinner.stop(); eventspinner.clear();
console.log(`${chalk.gray('[PROC]')} >> ${chalk.blue('Loaded all Events')}`);
let rspspinner = ora(chalk.blue('Loading Commands')).start();
let responses = fs.readdirSync('./responses').filter(file => file.endsWith('.js'));
client.responses.triggers = [];
for (let responsef of responses) {
if (Object.keys(require.cache).includes(require.resolve(`../../responses/${responsef}`))) {delete require.cache[require.resolve(`../../responses/${responsef}`)];}
let response = require(`../../responses/${responsef}`);
client.responses.triggers.push([response.name, response.condition]);
client.responses.commands.set(response.name, response);
}
rspspinner.stop(); rspspinner.clear();
console.log(`${chalk.gray('[PROC]')} >> ${chalk.blue('Loaded all Responses')}`);
console.log(`\n${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`Client refresh successful`)}\n`);
return message.channel.send(`Done! Reloaded ${commands.length} commands, ${eventFilter.length} events, and ${responses.length} responses in ${new Date().getTime() - timer}ms.`);
}
if (['l', 'log', 'ns', 'nosilent', 'notsilent'].includes(args[0].toLowerCase())) {
['commands', 'aliases'].forEach(x => client[x] = new Discord.Collection());
client.responses = {triggers: [], commands: new Discord.Collection()};
['command', 'event', 'response'].forEach(x => require(`./${x}`)(client));
return message.channel.send("Done!");
}
if (['c', 'cmd', 'command'].includes(args[0].toLowerCase())) {
let timer = new Date().getTime();
if (!args[1]) {return message.channel.send("Oi there you headass! You have to actually tell me what command to reload!");}
let tc = args[1].toLowerCase();
let lf = client.commands.get(tc) || client.commands.get(client.aliases.get(tc));
lf = lf ? lf.name : tc;
let res;
fs.readdirSync(`./commands`).forEach(x => {
if (!x.includes('.')) {fs.readdirSync(`./commands/${x}`).forEach(y => {if (`${lf}.js` === y) {res = `../../commands/${x}/${y}`;}});}
else {if (x === `${lf}.js`) {res = `../../commands/${x}`;}}
});
if (!res) {return message.channel.send("I can't reload that command as I can't find file!");}
if (require.resolve(res) in require.cache) {delete require.cache[require.resolve(res)];}
const ncmd = require(res);
client.commands.set(lf, ncmd);
if (ncmd.aliases) {ncmd.aliases.forEach(alias => {if (!client.aliases.has(alias)) {client.aliases.set(alias, lf);}});}
return message.channel.send(`Reloaded command \`${lf}\` in ${new Date().getTime() - timer}ms`);
}
else {return message.channel.send("Oi! 'log' is the only valid arg to use. Use no args if you want a cleaner console output instead.");}
}
};

@ -1,34 +0,0 @@
const Discord = require('discord.js');
const cp = require('child_process');
module.exports = {
name: "restart",
aliases: ['res'],
meta: {
category: 'Developer',
description: "Fully restart Natsuki",
syntax: '`restart`',
extra: null
},
help: "Fully restarts the bot. Developer-only.",
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!client.developers.includes(message.author.id)) {return message.channel.send("You must be a developer in order to do that!");}
try {
await require('../../util/lxp/cacheloop')(client);
await require('../../util/vcloop')(client);
await require('../../util/monitorloop')(client);
await message.channel.send("Cache synchronized with DB! Restarting...");
return cp.exec("pm2 restart natsuki", function(error, stdout, stderr) {
if (error) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Error")
.setDescription(`\`\`\`${error}\`\`\``)
.setColor("ff446a")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()]}
);
}
});
} catch {return message.channel.send("There was an error in trying to do that!");}
}
};

@ -1,42 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const {TagFilter} = require('../../util/tagfilter');
const {Tag} = require('../../util/tag');
module.exports = {
name: "setstatus",
aliases: ['sst'],
meta: {
category: 'Developer',
description: "Set my public status. Don't make me say something weird! (Also only available to devs, of course.)",
syntax: '`setstatus <-s status> <-t type>`',
extra: "You can check if a user is an admin without being a developer."
},
tags: [],
help: new Discord.MessageEmbed()
.setTitle("Help -> Status-Setting")
.setDescription("Sets the bot's status")
.addField("Syntax", "`setstatus <-s status> <-t type>`")
.addField('Notice', "This command is **developer-only**"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}setstatus <status> [type]\``);}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.developer) && !client.developers.includes(message.author.id)) {return message.channel.send("You must be my developer in order to do that!");}
let options = new TagFilter([
new Tag(['s', 'status', 'm', 'msg', 'message'], 'status', 'append'),
new Tag(['t', 'type'], 'type', 'append')
]).test(args.join(" "));
if (!options.status || !options.status.length) {return message.channel.send("You must use the -status tag (and -type if you want a custom type)!");}
if (options.status.length > 30) {return message.reply("That status is a bit too long.");}
if (options.type) {if (!['playing', 'watching', 'listening'].includes(options.type.toLowerCase())) {return message.channel.send("That's not a valid type!");}}
if (options.type) {client.user.setActivity(options.status, {type: options.type.toUpperCase()});}
else {client.user.setActivity(options.status);}
return message.channel.send(`Status set to: \`${options.type ? `${options.type.slice(0, 1).toUpperCase()}${options.type.slice(1).toLowerCase()}` : "Playing"} ${options.status}\``);
}
};

@ -1,42 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
module.exports = {
name: "staff",
help: new Discord.MessageEmbed()
.setTitle("Help -> Staff")
.setDescription("Make a user a Natsuki staff member")
.addField("Syntax", "`staff <add|remove|check> <@user|userID>`")
.addField("Notice", "This command is only available to Natsuki developers."),
meta: {
category: 'Developer',
description: "Add or remove users as Natsuki staff",
syntax: '`staff <add|remove|check> <@user|userID>`',
extra: "You can check if a user is staff without being a developer."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This is a guild-only command.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}\``);}
let person = mention ? mention : args[1] ? client.users.cache.has(args[1]) ? client.users.cache.get(args[1]) : null : null;
let tu = await UserData.findOne({uid: person ? person.id : message.author.id}) ? await UserData.findOne({uid: person ? person.id : message.author.id}) : new UserData({uid: person ? person.id : message.author.id});
if (['c', 'check'].includes(args[0])) {return message.reply(`${person ? person : message.member.displayName} ${tu.staff ? 'is' : 'is not'} a part of Natsuki Staff.`);}
if (!['a', 'add', 'r', 'remove'].includes(args[0])) {return message.reply("You must specify whether to `add` or `remove` someone as a Staff Member.");}
if (!person) {return message.reply("You must mention someone to add as a staff member, or use their ID.");}
let atu = await UserData.findOne({uid: message.author.id});
if ((!atu || !atu.developer) && !client.developers.includes(message.author.id)) {return message.reply('You must be a developer in order to add set staff member statuses.');}
if (['a', 'add'].includes(args[0])) {tu.support = true; tu.staff = true;}
else {tu.staff = false; tu.admin = false; tu.developer = false;}
tu.save();
const logemb = (act) => new Discord.MessageEmbed()
.setAuthor({name: `Staff ${act}`, iconURL: message.author.displayAvatarURL()})
.setDescription("A user's Staff status was updated.")
.setThumbnail(person.displayAvatarURL({size: 1024}))
.addField("Name", person.username, true)
.addField("Developer", message.author.username, true)
.setColor("e8da3a")
.setFooter({text: "Natsuki"})
.setTimestamp();
client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915').send(logemb(['a', 'add'].includes(args[0]) ? 'Added' : 'Removed'));
return message.reply(`${message.guild.members.cache.get(person.id).displayName} is no${['a', 'add'].includes(args[0]) ? 'w' : ' longer'} a staff member!`);
}
};

@ -1,42 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
module.exports = {
name: "supportstaff",
help: new Discord.MessageEmbed()
.setTitle("Help -> Support")
.setDescription("Make a user a Natsuki Support Team member")
.addField("Syntax", "`supportstaff <add|remove|check> <@user|userID>`")
.addField("Notice", "This command is only available to Natsuki admin."),
meta: {
category: 'Developer',
description: "Add or remove users as Natsuki support",
syntax: '`supportstaff <add|remove|check> <@user|userID>`',
extra: "You can check if a user is a support member without being a developer."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This is a guild-only command.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}supportstaff <add|remove|check> <@user|userID>\``);}
let person = mention ? mention : args[1] ? client.users.cache.has(args[1]) ? client.users.cache.get(args[1]) : null : null;
let tu = await UserData.findOne({uid: person ? person.id : message.author.id}) ? await UserData.findOne({uid: person ? person.id : message.author.id}) : new UserData({uid: person ? person.id : message.author.id});
if (['c', 'check'].includes(args[0])) {return message.reply(`${person ? person : message.member.displayName} ${tu.support ? 'is' : 'is not'} a part of Natsuki Support.`);}
if (!['a', 'add', 'r', 'remove'].includes(args[0])) {return message.reply("You must specify whether to `add` or `remove` someone as a Support Team Member.");}
if (!person) {return message.reply("You must mention someone to add as a support member, or use their ID.");}
let atu = await UserData.findOne({uid: message.author.id});
if (!atu || !atu.admin) {return message.reply('You must be an admin in order to add set support team member statuses.');}
if (['a', 'add'].includes(args[0])) {tu.support = true;}
else {tu.support = false; tu.staff = false; tu.admin = false; tu.developer = false;}
tu.save();
const logemb = (act) => new Discord.MessageEmbed()
.setAuthor({name: `Support ${act}`, iconURL: message.author.displayAvatarURL()})
.setDescription("A user's Support status was updated.")
.setThumbnail(person.displayAvatarURL({size: 1024}))
.addField("Name", person.username, true)
.addField("Developer", message.author.username, true)
.setColor("e8da3a")
.setFooter({text: "Natsuki"})
.setTimestamp();
client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915').send({embeds: [logemb(['a', 'add'].includes(args[0]) ? 'Added' : 'Removed')]});
return message.reply(`${message.guild.members.cache.get(person.id).displayName} is no${['a', 'add'].includes(args[0]) ? 'w' : ' longer'} a Support Team member!`);
}
};

@ -1,54 +0,0 @@
const Discord = require("discord.js");
module.exports = {
name: "vip",
aliases: ["premium"],
help: new Discord.MessageEmbed()
.setTitle("Help -> VIP")
.setDescription("Toggle a server as VIP. This grants a few small bonuses to your server that you can't get anywhere else!\n\nWant to become a VIP? Support the bot by [joining the support server](), donating to the bot's creators, or promoting/spreading the bot to other servers.")
.addField("Syntax", "`vip <add|remove|check>`")
.addField("Notice", "This command is **developer-only**."),
meta: {
category: 'Developer',
description: "Set server VIP status",
syntax: '`vip <add|remove|check>`',
extra: "This command is mostly cosmetic as there are no real perks *yet*"
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This command is server-only!");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}vip <add|remove|check>\``);}
if (!client.developers.includes(message.author.id) && !['check', 'c', 'view', 'v'].includes(args[0])) {return message.reply("Unfortunately, this is a **developer-only command**!");}
const GuildSettings = require('../../models/guild');
const logemb = (act) => new Discord.MessageEmbed()
.setAuthor({name: `VIP Server ${act}`, iconURL: message.author.displayAvatarURL()})
.setDescription("A Server's VIP status was updated.")
.setThumbnail(message.guild.iconURL({size: 1024}))
.addField("Name", message.guild.name, true)
.addField("Admin", message.author.username, true)
.setColor("e8da3a")
.setFooter({text: "Natsuki"})
.setTimestamp();
if (['add', 'a', 'make', 'm'].includes(args[0])) {
let tguild = await GuildSettings.findOne({gid: message.guild.id})
? await GuildSettings.findOne({gid: message.guild.id})
: new GuildSettings({gid: message.guild.id});
if (tguild.vip === true) {return message.reply("This server is already a VIP server.");}
tguild.vip = true;
tguild.save();
client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915').send({embeds: [logemb("Added")]});
return message.reply("This server is now a VIP server!");
} else if (['remove', 'r', 'delete', 'd'].includes(args[0])) {
let tguild = await GuildSettings.findOne({gid: message.guild.id});
if (tguild) {
if (tguild.vip === false) {return message.reply("This server wasn't a VIP server anyways...");}
await GuildSettings.findOneAndUpdate({gid: message.guild.id, vip: false});
client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915').send({embeds: [logemb("Removed")]});
} else {return message.reply("This server wasn't a VIP server anyways...");}
return message.reply("This server is no longer a VIP server!");
} else if (['check', 'c', 'view', 'v'].includes(args[0])) {
let tguild = await GuildSettings.findOne({gid: message.guild.id});
return message.reply((tguild && tguild.vip) ? 'This server is a VIP server.' : 'This server is not a VIP server.');
}
}
};

@ -1,36 +0,0 @@
const Discord = require("discord.js");
module.exports = {
name: "8ball",
aliases: ["8b"],
help: new Discord.MessageEmbed()
.setTitle("Help -> 8ball")
.setDescription("Gives you moral support, decides if you really do want that third taco, or helps you decide on your existential crisis. Answers come with an accuracy guarantee of 0%!")
.addField("Syntax", "`8ball <question>`"),
meta: {
category: 'Fun',
description: "Gives you moral support, decides if you really do want that third taco, or helps you decide on your existential crisis. Answers come with an accuracy guarantee of 0%!",
syntax: '`8ball <question>`',
extra: null
},
execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}8ball <question>\``);}
let question = args.join(" ");
if (question.length > 230) {return message.reply("Listen, I'm no fortune-teller. I can't help you with a question that long!");}
let responses = [
/*Positive Responses*/ "Yes!", "I think so", "Possibly", "Certainly", "Definitely", "Absolutely", "Sure!", "Most Likely", "I believe so", "If you're asking for my honest opinion... yes"
/*Negative Responses*/ ,"No!", "I don't think so", "Probably not", "Impossible", "Nope", "Absolutely *not*", "Nah", "Doubt it", "I don't believe so", "If you're asking for my honest opinion... no"
/*Neutral Responses */ ,"Maybe", "I'm not sure", "I'll think about it", "Uhh Natsuki isn't here right now. I can take a message?", "I'm sure if you look deep within your heart, which is currently all over that tree, you'll find the answer", "I mean, if you think so...", "I don't have an opinion on that.", "I'll choose to remain silent."
];
let name = message.guild ? message.member.displayName : message.author.username;
return message.reply({embeds: [new Discord.MessageEmbed()
.setAuthor({name: "8ball Question", iconURL: message.author.displayAvatarURL()})
.setDescription("**Question:** " + question + "\n**Answer:** " + responses[Math.floor(Math.random() * responses.length)])
.setColor("c375f0")
.setFooter({text: `Asked by ${name} | Natsuki`})
.setTimestamp()]}
);
}
};

@ -1,47 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const makeId = require('../../util/makeid');
module.exports = {
name: "bite",
aliases: [],
help: "Use `{{p}}slap @person` to have me personally deliver your anger to them with a nice s l a p.",
meta: {
category: 'Fun',
description: "Slap another user! Virtually, of course.",
syntax: '`slap <@user>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'bite'}) || new Saves({name: 'bite'});
let saves = savess.saves;
if (!args.length) {
return message.channel.send(message.guild ? "Please mention someone to bite!" : "Oi! I get it if you don't like me but you can't just waltz into my DMs and bite me!");}
if (mention && args[0].match(/^<@!?\d+>$/)) {
if (!message.guild) {return message.reply("Oi! I get it if you don't like me but you can't just waltz into my DMs and bite me!");}
if (!message.guild.members.cache.has(mention.id)) {return message.reply("That user is not in this server!");}
if (message.author.id === mention.id) {return message.reply("Ew quit tryna bite yourself, that's weird.");}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: `${message.guild ? message.member.displayName : message.author.username} bites ${message.guild.members.cache.get(mention.id).displayName}`, iconURL: message.author.displayAvatarURL()})
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('d93846')
]});
}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staff) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Staff member in order to add new bite GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("Save added!");
}
}
};

@ -1,147 +0,0 @@
const Discord = require('discord.js');
const moment = require('moment');
const VC = require('../../models/vscount');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
const deaths = [
"watching too much anime", "an overdose of waifus", "Hypotakunemia", "trying to self-isekai",
"Bass:tm:", "cranking the music just a little too loud", "trying to swim in lava",
"Despacito", "something really cliche and unoriginal", "'shrooms",
"clicking 'I agree' without reading the Terms of Service", "alchemy", "rusty spoons",
"picking the wrong waifu", "body pillows", "fur-con", "something to do with lamb sauce",
"grandma's cookies", "trying to get cat ears", "not reading the assembly instructions for that ikea furniture",
"the wrong kind of coke", "getting cancelled irl", "getting their credit card declined",
"finishing the last episode", "posting memes in #general", "stepping on a lego", "stubbing their toe",
"fork in toaster", "toasterbath", "signing this book without reading the cover", "being Kirby's dinner"
]; // a list of preset death methods that can occur
const before = [
"A name is being written...", "Someone will perish soon...", "A body is *about* to be discovered...",
"{w} is scribbling something in their notebook...", "\*Manical laughter echoes around you*...",
"{w} laughs maniacally..."
]; // things it says before the response is sent
const responses = {
/*an obj controlling the possible formats for the death note report*/
news: {
titles: ["Breaking News"],
texts: [
"This just in: **{p}** was found dead at **{ds}** today.\n\nAfter some investigation, the authorities ruled the cause of death to be **{c}**.",
"We're now live at the crime scene where it is believed that **{p}** died because of **{c}**.",
"Authorities are reporting what is believed to be another Kira case, where **{c}** has taken the life of **{p}**."
],
images: [],
}, // a news report of the dead body
writes: {
titles: ["Something sinister just happened", "A name has been written", "Fate has been changed"],
texts: [
"With a maniacal laugh, **{w}** writes \"**{p}**\" in their Death Note. And the cause of death? They've written **{c}**.",
"**{w}** has sealed **{pa}** fate to die by **{c}**."
],
images: []
}, // "so-and-so writes blah blah blah's name in their death note"
/*hasdied: {
texts: [],
images: []
}, // "so-and-so has died by...",
unserious: {
texts: [],
images: []
} // other methods, mainly the un-serious or joking ones */
};
//responses.unserious.images = responses.hasdied.images;
module.exports = {
name: "deathnote",
aliases: ['dn'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Death Note")
.setDescription("Congratulations! You've picked up a death note. Write someone's name in it, and see for yourself if it's the real deal...")
.addField("Syntax", "\`deathnote <@member> [method of death]\`"),
meta: {
category: 'Fun',
description: "Write someone's name in your deathnote. I'm not legally responsible for anything that happens after that.",
syntax: '`deathnote <@member> [method of death]`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("Unfortunately, this is a **guild-only** command!");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}deathnote <@member> [method of death]\``);}
if (args[0] === "kill" || args[0] === "k") {args.shift();} // if someone adds in 'kill' it'll remove it and act like it wasn't there, proceeding as normal.
//if (!args[0].trim().match(/^<@(?:\!?)\d+>$/)) {return message.reply("You have to mention someone!");}
if (mention && mention.id === message.author.id) {return message.reply("Hehe I won't let you write your own name in the notebook! Just leave it somewhere for a few days and someone else will take it. Maybe they'll write your name...");} // users can't mention themselves
if (mention && mention.id === client.user.id) {return message.reply("You can't kill me! Little did you know, I'm actually a death god!");}
if (mention && mention.bot) {return message.reply("As a bot, I simply cannot let you attempt to kill another fellow bot!");}
let reptype = responses[Object.keys(responses)[Math.floor(Math.random() * Object.keys(responses).length)]]; // report type
let title = reptype.titles[Math.floor(Math.random() * reptype.titles.length)];
let options = new TagFilter([
new Tag(['method', '-m', 'cause', '-c'], 'method', 'append'),
new Tag(['victim', 'v', 'against', 'a', 'name', 'n'], 'victim', 'append')
]).test(args.join(" "));
let death = (!options.victim || (options.victim && !options.victim.length)) && (!options.method || (options.method && !options.method.length)) && args.length > 1
? args.join(" ").slice(args[0].length + 1)
: deaths[Math.floor(Math.random() * deaths.length)]; //kill method
if (options.method && options.method.length) {death = options.method;}
if (death.length > 750) {return message.channel.send("I'd rather you didn't try to fill the death note with a 7-page double-spaced essay in Times New Roman containing an advanced trajectory theorem on the death of your poor target.");}
if (!mention && (!options.victim || !options.victim.length)) {return message.reply("You have to write their name down in order to kill them! (In other words, please mention the user whose name you wish to write.)");}
if (options.victim && options.victim.length) {
let vargs = options.victim.trim().split(/\s+/g);
let nvargs = [];
let varg; for (varg of vargs) {
if (varg.match(/^<@(?:!?)\d+>$/)) {
nvargs.push(message.guild.members.cache.has(varg.slice(varg.search(/\d/), varg.search('>'))) ? message.guild.members.cache.get(varg.slice(varg.search(/\d/), varg.search('>'))).displayName : varg);
} else {nvargs.push(varg);}
}
options.victim = nvargs.join(" ").trim();
}
let victim = options.victim && options.victim.length ? options.victim : message.mentions.members.first().displayName;
let killer = message.member;
let pretext = before[Math.floor(Math.random() * before.length)].replace(/{w}/g, killer.displayName);
let note = await message.channel.send({embeds: [new Discord.MessageEmbed()
.setDescription(pretext)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
await require('../../util/wait')(2500);
let text = reptype.texts[Math.floor(Math.random() * reptype.texts.length)]
.replace(/{p}/g, victim) //{p} = victim
.replace(/{pa}/g, victim.toLowerCase().endsWith('s') ? `${victim}'` : `${victim}'s`) //{pa} = victim but with a formatted apostrophe like "WubzyGD's"
.replace(/{c}/g, death) // {c} = death method
.replace(/{w}/g, killer.displayName) // {w} = killer or writer
.replace(/{ds}/g, moment().format("h:mm a")); // {ds} = date small, basically just the time.
// Create and format the kill text
let dns;
if (mention && mention.id) {
dns = await VC.findOne({uid: message.author.id, countOf: 'dn'}) || new VC({uid: message.author.id, countOf: 'dn'});
dns.against[mention.id] = dns.against[mention.id] ? dns.against[mention.id] + 1 : 1;
dns.total++;
dns.markModified(`against.${mention.id}`);
dns.save();
}
let finalEmbed = new Discord.MessageEmbed()
.setAuthor({name: title, iconURL: message.author.displayAvatarURL()})
.setDescription(`${text}${dns ? `\n\n_Their name is in your deathnote **${dns.against[mention.id] === 1 ? 'once' : `${dns.against[mention.id]} times`}.**_` : ''}`)
.setColor('c375f0')
.setFooter({text: `Natsuki${dns ? ` | ${dns.total} name${dns.total === 1 ? ' has been' : 's'} written in your deathnote!` : ''}`})
.setTimestamp();
if (mention) {finalEmbed.setThumbnail(mention.displayAvatarURL({size: 1024}));}
return note.edit({embeds: [finalEmbed]});
}
};

@ -1,88 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const ask = require("../../util/ask");
module.exports = {
name: "marry",
meta: {
category: 'Fun',
description: "Marry someone to get some extra fun benefits!",
syntax: '`marry <@user|status|decline>`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Marriage")
.setDescription("Marry another Natsuki user. They'll have to accept your marriage request for you to be able to marry them, though.")
.addField("Syntax", "`marry <@user|status|decline>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}marry <@user|status|decline>\``);}
if (['s', 'status', 'v', 'view'].includes(args[0].toLowerCase())) {
const tu = await UserData.findOne({uid: message.author.id});
if (!tu || !tu.marriedTo) {return message.channel.send("You aren't married to anyone :(");}
else {return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Marriage Status")
.setDescription(`<@${message.author.id}> is married to <@${tu.marriedTo}>!`)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.avatarURL()})
.setTimestamp()
]});}
} else if (['d', 'decline', 'r', 'reject'].includes(args[0].toLowerCase())) {
let requests = client.misc.cache.marriageRequests.filter(u => u === message.author.id);
if (!requests.size) {return message.channel.send("You don't have any people trying to marry you!");}
let ra = Array.from(requests.keys());
message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Current Marriage Requests")
.setDescription(ra.map((r, i) => `${i+1}. <@${r}>`).join("\n"))
.addField("Decline", "To decline someone's request, please reply with the number that corresponds to the person you'd like to decline.")
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.avatarURL()})
.setTimestamp()
]});
try {
let res = await message.channel.awaitMessages({filter: (m) => m.author.id === message.author.id, max: 1, errors: ['time'], time: 60000});
res = res.first().content;
if (isNaN(Number(res))) {return message.channel.send("You must reply with a number!");}
res = Number(res);
if (res < 1 || res > ra.length + 1) {return message.channel.send("That number isn't in your list.");}
let person = ra[res-1];
let err = false;
await message.guild.members.fetch(person).catch(() => err = true);
if (err) {return message.channel.send("There was an error in trying to do that!");}
let conf = await ask(message, `Are you sure you want to decline ${client.utils.ps(message.guild.members.cache.get(person).displayName)} marriage request?`, 60000);
if (!conf) {return;}
if (!['y', 'yes', 'ye', 'sure', 'mhm'].includes(conf.toLowerCase())) {return message.channel.send("Okay, nevermind then!");}
client.misc.cache.marriageRequests.delete(person);
return message.channel.send("Request declined.");
} catch {return message.reply("That request timed out. Please try again.");}
} else {
if (!mention) {return message.channel.send("You have to mention the person you'd like to marry!");}
if (!message.guild.members.cache.has(mention.id)) {return message.channel.send("I can't find that user! Make sure they're in this server before trying to marry them, or go to a server they're in. If you're certain that person is in this server, then wait for them to come online and send a message first; that might help.");}
if (mention.id === client.user.id) {return message.channel.send("I'm actually already married! Well, I will be soon, to my girlfriend Tamaki. I love her very much <:NC_hearty:841489530413383712>");}
if (mention.bot) {return message.channel.send("Us bots aren't smart enough to respond to a marriage request and we're really too boring to wanna marry in the first place, so I'll just stop you in your tracks now.");}
if (mention.id === message.author.id) {return message.channel.send("ehe :sweat_smile: it doesn't work that way...");}
const tu = await UserData.findOne({uid: message.author.id}) || new UserData({uid: message.author.id});
if (tu.marriedTo) {return message.channel.send("Looks like you're already married to someone. Cheating, are we?");}
const ou = await UserData.findOne({uid: mention.id}) || new UserData({uid: mention.id});
if (ou.marriedTo) {return message.channel.send("Looks like that person is already in a relationship. Yikes, good luck with that.");}
if (client.misc.cache.marriageRequests.has(ou.uid) && client.misc.cache.marriageRequests.get(ou.uid) === message.author.id) {
tu.marriedTo = ou.uid; ou.marriedTo = tu.uid;
tu.markModified('marriedTo'); ou.markModified('marriedTo');
await tu.save(); await ou.save();
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`New Marriage!`)
.setDescription(`With the powers invested in me by Wubzy ~~and myself because I'm cool like that~~, I now pronounce ${message.member.displayName} and ${message.guild.members.cache.get(ou.uid).displayName} a married couple till debt and richer Discord users do you part.`)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.avatarURL()})
.setTimestamp()
]});
}
if (client.misc.cache.marriageRequests.has(message.author.id)) {return message.channel.send("You're already waiting on another marriage response.");}
if (client.misc.cache.marriageRequests.filter(u => u === ou.uid).size > 10) {return message.channel.send("*10 people are waiting on a response to marry this person. Yikes.*");}
client.misc.cache.marriageRequests.set(message.author.id, ou.uid);
return message.channel.send(`<@${ou.uid}>, you have a marriage request from ${message.member.displayName}. Send \`${prefix}marry @${message.member.displayName}\` to accept!`);
}
}
};

@ -1,50 +0,0 @@
const Discord = require('discord.js');
const Saves = require("../../models/saves");
module.exports = {
name: "nowplaying",
aliases: ['np'],
meta: {
category: 'Fun',
description: "Show off the music you're currently listening to through last.fm!",
syntax: '`nowplaying [@mention]`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Now Playing")
.setDescription("Accesses last.fm's API to show off what music you're currently listening to. Use the `lfm` command for more information.")
.addField("Syntax", "`nowplaying [@mention]`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'lfm'}) ? await Saves.findOne({name: 'lfm'}) : new Saves({name: 'lfm'});
let saves = savess.saves;
let user = mention || message.author;
if (!saves.get(user.id)) {return message.channel.send(`${mention ? "That person's" : "Your"} last.fm username isn't set! ${mention ? "They" : "You"} can set it with \`${prefix}lfm set <username>\``);}
let found = false;
try {
const glfm = function() {return new Promise(resolve => {
let timeout = setTimeout(() => {return resolve(undefined);}, 3000);
let stream = client.lfm.stream(saves.get(user.id));
stream.on('nowPlaying', t => {
clearTimeout(timeout);
message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: message.guild ? message.guild.members.cache.get(user.id) ? message.guild.members.cache.get(user.id).displayName : user.username : user.username, iconURL: user.displayAvatarURL()})
.setTitle(`${saves.get(user.id)} | Now Playing`)
.setDescription(`<@${user.id}> is currently listening to **${t.name}** by **${t.artist['#text']}**.\nView the song [here](${t.url}).`)
.setColor("c375f0")
.setThumbnail(t.image[3]['#text'])
.setTimestamp()
]})
found = true;
stream.stop();
return resolve(undefined);
});
stream.start();
});}
await glfm().catch((e) => {console.error(e);});
} catch (e) {console.error(e);}
if (!found) {return message.channel.send(`I couldn't find what \"${saves.get(user.id)}\" is listening to. Perhaps ${!mention ? "you" : "they"}'re not listening to anything, or you got ${!mention ? "your" : "their"} name wrong?`);}
}
};

@ -1,181 +0,0 @@
const Discord = require('discord.js');
const RP = require("../../models/rpch");
const RPC = require('../../models/rpconfig');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
const ask = require('../../util/ask');
module.exports = {
name: "rp",
aliases: ['roleplay'],
meta: {
category: 'Fun',
description: "Add and edit characters for roleplaying",
syntax: '`rp <add|enable|edit|delete|view|list>`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Roleplay Characters")
.setDescription("Add and edit characters to roleplay with!")
.addField("Syntax", "`rp <add|enable|edit|delete|view|list>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}rp <add|enable|edit|delete|view|list>\``);}
if (['add', 'a', 'n', 'new'].includes(args[0].toLowerCase())) {
let tags = true;
let options = new TagFilter([
new Tag(['i', 'img', 'image', 'pfp', 'thumbnail', 't', 'thumb'], 'image', 'append'),
new Tag(['n', 'name'], 'name', 'append'),
new Tag(['p', 'prefix'], 'prefix', 'append')
]).test(args.join(" "));
if (!options.image || !options.name || !options.prefix) {
tags = false;
const name = await ask(message, "What is the character's name?", 60000, false, undefined, true); if (!name) {return;}
if (name.length > 50) {return message.channel.send("That name is a little too long.");}
options.name = name;
let prefix = await ask(message, "What is the character's prefix? This is how you will use the character.", 60000, false, undefined, true); if (!prefix) {return;}
if (prefix.length > 8) {return message.channel.send("Your prefix should be less than 8 characters.");}
if (!prefix.match(/^[a-zA-Z0-9-_]+$/)) {return message.channel.send("Your prefix must contain only alphanumeric characters.");}
options.prefix = prefix.toLowerCase();
let imp = await message.channel.send("Please send an image for the character's profile picture. **Do not** send NSFW.");
try {
const im = await message.channel.awaitMessages({filter: m => m.author.id === message.author.id, time: 60000, max: 1, errors: ['time']});
imp.delete().catch(() => {});
if (!im.first().attachments.size) {return message.channel.send("You must directly upload an image to use for the profile picture.");}
options.image = im.first().attachments.first().url;
}
catch {imp.delete().catch(() => {}); return message.reply("Your character creation timed out.");}
} else {
if (options.prefix.length > 8) {return message.channel.send("Your prefix should be less than 8 characters.");}
if (!options.prefix.match(/^[a-zA-Z0-9-_]+$/)) {return message.channel.send("Your prefix must contain only alphanumeric characters.");}
}
if (!options.image.startsWith('https://cdn.discordapp.com/attachments/')) {return message.channel.send(tags ? "You must provide a cdn.discordapp.com link." : "It seems you didn't upload an image, or there was an error on my side. If the problem persists, please contact my developers.");}
if (options.image.length > 350) {return message.channel.send("That image URL is a little too long.");}
let rp = await RP.findOne({uid: message.author.id}) || new RP({uid: message.author.id});
if (rp.chars[options.prefix]) {return message.channel.send("You already have a character with that prefix. Please try again with a different prefix.");}
if (Object.keys(rp.chars).length >= 20) {return message.channel.send("The current maximum of characters is 20, and you've reached that maximum. Sorry!");}
rp.chars[options.prefix] = options;
rp.markModified(`chars.${options.prefix}`);
rp.save();
return message.channel.send(`${options.name} has been added successfully to your characters list. To use this character, you'll need to be in a channel that is RP-enabled. \`${options.prefix}: <message>\``);
} else if (['list', 'l'].includes(args[0].toLowerCase())) {
let rp = await RP.findOne({uid: message.author.id});
if (!rp) {return message.channel.send("You don't have any characters made!");}
let n = message.member ? message.member.displayName : message.author.username;
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: `${n}${n.endsWith('s') ? "'" : "'s"} RP Characters`, iconURL: (message.member || message.author).displayAvatarURL({dynamic: true})})
.setDescription(Object.keys(rp.chars).map(ch => `\`${ch}\`: ${rp.chars[ch].name}`).join(', '))
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
} else if (['view', 'v'].includes(args[0].toLowerCase())) {
let rp = await RP.findOne({uid: message.author.id});
if (!rp) {return message.channel.send("You don't have any characters made!");}
if (!args[1] || !rp.chars[args[1].toLowerCase()]) {return message.channel.send("You don't have a character with that prefix.");}
let ch = rp.chars[args[1]];
let n = message.member ? message.member.displayName : message.author.username;
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: `${n}${n.endsWith('s') ? "'" : "'s"} RP Character`, iconURL: (message.member || message.author).displayAvatarURL({dynamic: true})})
.setTitle(ch.name)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setImage(ch.image)
.setTimestamp()
]});
} else if (['edit', 'e'].includes(args[0].toLowerCase())) {
const rp = await RP.findOne({uid: message.author.id});
if (!rp || !Object.keys(rp.chars).length) {return message.channel.send("You don't have any characters to edit.");}
if (!args[1]) {return message.channel.send("You must provide the prefix of a character to edit!");}
if (!rp.chars[args[1].toLowerCase()]) {return message.channel.send("A character with that prefix doesn't exist.");}
let char = rp.chars[args[1].toLowerCase()];
if (!args[2]) {return message.channel.send(`Syntax: \`${prefix}rp edit <char> <prefix|image|name>\``);}
if (['p', 'prefix'].includes(args[2].toLowerCase())) {
let old = char.prefix;
let prefix;
if (!args[3]) {
prefix = await ask(message, "What prefix would you like to set?", 60000);
if (!prefix) {return;}
} else {prefix = args[3];}
prefix = prefix.toLowerCase();
if (prefix.length > 8) {return message.channel.send("Your prefix should be less than 8 characters.");}
if (!prefix.match(/^[a-zA-Z0-9-_]+$/)) {return message.channel.send("Your prefix must contain only alphanumeric characters.");}
if (prefix === old) {return message.channel.send("That's the same prefix you already had, silly!");}
char.prefix = prefix;
delete rp.chars[old];
rp.chars[prefix] = char;
['chars', `chars.${old}`, `chars.${prefix}`].forEach(x => rp.markModified(x));
rp.save();
return message.channel.send("Prefix saved!");
} else if (['i', 'image', 'img'].includes(args[2].toLowerCase())) {
let image;
if (!args[3]) {
image = await ask(message, "What image would you like to set?", 60000);
if (!image) {return;}
} else {image = args[3];}
if (!image.startsWith('https://cdn.discordapp.com/attachments/')) {return message.channel.send("You must provide a cdn.discordapp.com link.");}
if (image.length > 350) {return message.channel.send("That image URL is a little too long.");}
rp.chars[char.prefix].image = image;
rp.markModified(`chars.${char.prefix}`);
rp.save();
return message.channel.send("Image saved!");
} else if (['n', 'name'].includes(args[2].toLowerCase())) {
args = args.slice(3);
let name;
if (!args[0]) {
name = await ask(message, "What name would you like to set?", 60000);
if (!name) {return;}
} else {name = args.join(" ");}
if (name.length > 50) {return message.channel.send("That name is a little too long.");}
rp.chars[char.prefix].name = name;
rp.markModified(`chars.${char.prefix}`);
rp.save();
return message.channel.send("Name saved!");
} else {return message.channel.send(`Invalid arg! Syntax: \`${prefix}rp edit <char> <prefix|image|name>\``);}
} else if (['delete', 'd'].includes(args[0].toLowerCase())) {
const rp = await RP.findOne({uid: message.author.id});
if (!rp || !Object.keys(rp.chars).length) {return message.channel.send("You don't have any characters to delete.");}
if (!args[1]) {return message.channel.send("You must provide the prefix of a character to delete!");}
if (!rp.chars[args[1].toLowerCase()]) {return message.channel.send("A character with that prefix doesn't exist.");}
let char = rp.chars[args[1].toLowerCase()];
let conf = await ask(message, `Are you sure you want to delete ${char.name}?`, 60000);
if (!conf) {return;}
if (!['yes', 'y', 'sure', 'mhm', 'ye'].includes(conf.toLowerCase())) {return message.channel.send("Okay, I won't do anything then.");}
delete rp.chars[char.prefix];
rp.markModified(`chars.${char.prefix}`);
rp.save();
return message.channel.send("I've deleted that character for you.");
} else if (['enable', 'en'].includes(args[0].toLowerCase())) {
if (!message.guild) {return message.channel.send("You must be in a server in order to enable RP character usage for a specific channel.");}
if (!message.member.permissionsIn(message.channel.id).has("MANAGE_WEBHOOKS")) {return message.channel.send("You must have permissions to edit webhooks here in order to do that.");}
if (!message.guild.me.permissions.has("MANAGE_WEBHOOKS")) {return message.channel.send("I don't have permissions to manage webhooks in this server.");}
if (!message.guild.me.permissionsIn(message.channel.id).has("MANAGE_WEBHOOKS")) {return message.channel.send("I don't have the permissions to edit webhooks in this channel.");}
const webhooks = await message.channel.fetchWebhooks();
if (webhooks.find(wh => wh.token)) {return message.channel.send("It would seem RP is already enabled in this channel. If it's not working in this channel, please contact my developers.");}
try {
return message.channel.createWebhook("Natsuki RP Webhook", {avatar: client.user.avatarURL({size: 2048})})
.then(async () => {
const config = await RPC.findOne({gid: message.guild.id}) || new RPC({gid: message.guild.id});
config.channels.push(message.channel.id);
config.markModified('channels');
if (!client.misc.cache.rp.has(message.guild.id)) {client.misc.cache.rp.set(message.guild.id, []);}
client.misc.cache.rp.get(message.guild.id).push(message.channel.id);
config.save();
return message.channel.send("RP features were successfully enabled in this channel.");
})
.catch(message.channel.send("There was an error doing that. Please make sure my permissions are properly set in this channel and try again. If the error persists, please contact my developers."));
} catch {return message.channel.send("There was an error doing that. Please make sure my permissions are properly set in this channel and try again. If the error persists, please contact my developers.");}
}
return message.channel.send(`Invalid arg! Syntax: \`${prefix}rp <add|enable|edit|delete|view|list>\``);
}
};

@ -1,182 +0,0 @@
const Discord = require('discord.js');
const SS = require('../../models/secretsanta');
const ask = require('../../util/ask');
module.exports = {
name: "secretsanta",
aliases: ['ss'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Secret Santa")
.setDescription("Create a secret santa for all of your friends or for your server! Whether you celebrate the holidays or not, this can still be loads of fun!")
.addField("Syntax", "`secretsanta <create|join|start|end|kick>`"),
meta: {
category: 'Fun',
description: "Create and join fully-functioning secret santas. I even randomize the assignments for you, how neat!",
syntax: '`secretsanta <create|join|start|end|kick>`',
extra: "It's not Christmas anymore, but you know what, who cares. Some features of this command, such as `end` and `kick` are not available/not working."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}secretsanta <create|join|start|end|kick>\``);}
if (['create', 'new', 'c', 'n', 's'].includes(args[0].toLowerCase())) {
function clearDM() {client.misc.activeDMs.delete(message.author.id);}
if (client.misc.activeDMs.has(message.author.id)) {return message.reply("I'm already asking you questions in DM for a separate command! Finish that command before using this one.");}
client.misc.activeDMs.set(message.author.id, 'secretsanta-make');
let mesg = await message.author.send("I'll be asking you a few questions here about how you want your Secret Santa to go! You can simply ignore the messages for a few minutes to cancel the process.").catch(() => {message.reply("Please open your DMs so I can ask you some questions!");});
let dmch = mesg.channel;
let conf = await ask(mesg, "This secret santa will be tied to your account, and you will be considered the host. Is this okay?", 60000, true); if (!conf) {return clearDM();}
if (['n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Oh, alrighty! Have the person who wants to be the host execute this same command.");}
if (!['yes', 'ye', 'y', 'sure'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Please specify yes or no you weeb!");}
let start = await ask(mesg, "When will you begin the secret santa? (You'll start it manually, so don't worry about formatting.", 60000, true); if (!start) {return clearDM();}
if (start.length > 150) {clearDM(); return dmch.send("Heya there, just a few words, please! I don't wanna have to read out an essay about when it's starting to all the people that want to hear about your secret santa!");}
let end = await ask(mesg, "When will you end the secret santa? (You'll also end it manually.)", 60000, true); if (!end) {return clearDM();}
if (end.length > 150) {clearDM(); return dmch.send("Heya there, just a few words, please! I don't wanna have to read out an essay about when it's ending to all the people that want to hear about your secret santa!");}
let spend = await ask(mesg, "What is your maximum and minimum spending? This is useful so that everyone gets an equal gift or gifts. This will be shown to the people that buy their gifts.", 360000, true); if (!spend) {return clearDM();}
if (spend.length > 500) {clearDM(); return dmch.send("Mate, this is not a dissertation! Let's keep it under 500 characters, please!");}
let anon = await ask(mesg, "Will you be keeping this secret santa totally anonymous, or will you let the gift recipients know who their gifters are when they are opened? Type \"yes\" if you will be keeping it anonymous, and \"no\" otherwise.", 360000, true); if (!anon) {return clearDM();}
if (['n', 'no'].includes(anon.trim().toLowerCase())) {anon = false;}
else if (['yes', 'ye', 'y', 'sure'].includes(anon.trim().toLowerCase())) {anon = true;}
else {clearDM(); return dmch.send("Please specify yes or no you weeb!");}
let info = await ask(mesg, "What information would you like me to ask joining members to provide when they join your secret santa? (Whatever you type will be shown directly to them when.)", 360000, true); if (!info) {return clearDM();}
if (info.length > 750) {clearDM(); return dmch.send("Let's keep it under 750 characters, please.");}
let notes = await ask(mesg, "Are there any other notes you'd like to add? If not, just write N/A or something of that nature.", 360000, true); if (!ask) {return clearDM();}
if (notes.length > 500) {clearDM(); return dmch.send("Let's keep it under 500 characters, please.");}
let fconf = await ask(mesg, "Would you like to continue and create the secret santa? By doing so, you agree that:\n-Natsuki and its developers are not responsible for anything financially-related to your secret santa\n-Anyone with your join code can join the secret santa\n-You are responsible for notifying your members of any changes or updates.\n-I am not responsible for any eggnog that may or may not be stolen from you by Wubzy. *for legal reasons this is a joke*\n\n-The answers you have submitted are what you want to use, as they cannot be changed.", 120000, true); if (!fconf) {return clearDM();}
if (['n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Oh, yikes. Is it about the eggnog? Sorry about that hehe...");}
if (!['yes', 'ye', 'y', 'sure'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Please specify yes or no you weeb!");}
let id;
while (true) {
id = require('../../util/makeid')(6);
let test = await SS.findOne({ssid: id});
if (!test) {break;}
}
let tss = new SS({
ssid: id,
owner: message.author.id,
start: start,
end: end,
anon: anon,
spend: spend,
info: info,
notes: notes,
members: [],
started: false
});
tss.save();
clearDM();
return dmch.send({embeds: [new Discord.MessageEmbed()
.setTitle("Secret Santa Created!")
.setDescription("Your Secret Santa has been completed! Have your members join by using `n?secretsanta join <ID>` where the ID is the ID displayed below. You can start your secret santa when you have at least 3 members with `n?secretsanta start <ID>`. If someone joins that you don't want in your secret santa, use `n?secretsanta kick <ID> <@member|userID>`. If you want to also participate, just join the same way as everyone else.")
.setThumbnail(message.author.displayAvatarURL({size: 1024}))
.addField("ID", `\`${id}\``, true)
.addField("Owner", message.author.username, true)
.setColor("01bd2f")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
if (['j', 'join'].includes(args[0].toLowerCase())) {
if (!args[1] || args[1].length !== 6) {return message.channel.send("You must provide a 6-digit join code! Ask the host to copy it and send it to you.");}
let tss = await SS.findOne({ssid: args[1]});
if (!tss) {return message.channel.send("A secret santa with that join code does not exist!");}
if (tss.started) {return message.channel.send("That secret santa has already started.");}
let min = false; let m; for (m of tss.members) {if (m.id === message.author.id) {min = true;}}
if (tss.members && min) {return message.channel.send("You're already in that secret santa!");}
function clearDM() {client.misc.activeDMs.delete(message.author.id);}
if (client.misc.activeDMs.has(message.author.id)) {return message.reply("I'm already asking you questions in DM for a separate command! Finish that command before using this one.");}
client.misc.activeDMs.set(message.author.id, 'secretsanta-join');
let mesg = await message.author.send("I'll be asking you a few questions here about you and what you want! You can simply ignore the messages for a few minutes to cancel the process.").catch(() => {message.reply("Please open your DMs so I can ask you some questions!");});
let dmch = mesg.channel;
let o = await client.users.fetch(tss.owner);
await dmch.send({embeds: [new Discord.MessageEmbed()
.setTitle("This Secret Santa!")
.setDescription("This is the one you're trying to join!")
.addField("Start", tss.start)
.addField("End", tss.end)
.addField("Spending", tss.spend)
.addField("Notes", tss.notes)
.addField("Anonymous Gifters", tss.anon ? "Yes" : "No")
.addField("ID", `\`${tss.ssid}\``, true)
.addField("Owner", o.username, true)
.addField("Members", `${tss.members ? tss.members.length : 0}`, true)
.setColor("01bd2f")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
let name = await ask(mesg, "What is your name? This can be seen by everyone in the secret santa.", 60000, true); if (!name) {return clearDM();}
if (name.length > 50) {clearDM(); return dmch.send("Maybe just the *first* name? I doubt it's over 50 characters.");}
await dmch.send("This is the information the host has asked you to provide:");
let info = await ask(mesg, tss.info, 600000, true); if (!info) {return clearDM();}
if (info.length > 750) {clearDM(); return dmch.send("Let's keep that under 750 characters. No need to put your entire Christmas list on there :smirk:");}
let conf = await ask(mesg, "Before we finish, do you agree to the following things:\n-I, Natsuki, and my developers, are not responsible for anything financially-related to your Secret Santa\n-You should contact the host if you have questions\n-These answers you gave are final and will be seen by the person who draws you.\n-You *need* to have your DMs open so that I can reach you when drawing time comes!", 120000, true);
if (['n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Alrighty! I've discarded your responses :P");}
if (!['yes', 'ye', 'y', 'sure'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Please specify yes or no you weeb!");}
let tssmembers = tss.members ? tss.members : {};
tssmembers.push({id: message.author.id, name: name, info: info});
tss.members = tssmembers;
tss.save();
clearDM();
await o.send(`${message.author.username} has joined your Secret Santa! (Code: \`${tss.ssid}\` in case you have more than one)`);
return dmch.send("All done! You've now joined.");
}
if (['start'].includes(args[0])) {
if (!args[1] || args[1].length !== 6) {return message.channel.send("You must specify the join code/ID to your Secret Santa!");}
let tss = await SS.findOne({ssid: args[1]});
if (!tss) {return message.channel.send("That Secret Santa doesn't exist; your code is invalid!");}
if (tss.owner !== message.author.id) {return message.channel.send("You must be the host to do that!");}
if (tss.started) {return message.channel.send("Your Secret Santa is already started!");}
if (tss.members.length < 3) {return message.channel.send("You need to have at least 3 members in order to start.");}
let dm = []; let rm;
let m; for (m of tss.members) {dm.push({name: m.id, assignedTo: null});}
for (m of dm) {
while (true) {
let rm = tss.members[Math.floor(Math.random() * tss.members.length)];
let exists = false;
let cdm; for (cdm of dm) {if (!exists) {exists = cdm.assignedTo === rm.id;}}
if (!exists && rm.id !== m.name) {dm[dm.indexOf(m)] = {name: m.name, assignedTo: rm.id}; break;}
}
}
tss.assignments = dm;
tss.started = true;
tss.save();
for (m of tss.members) {
let mem = await client.users.fetch(m.id);
let assignmentt; let a; for (a of tss.assignments) {if (a.name === m.id) {assignmentt = a.assignedTo; break;}}
let assignment; let tm; for (tm of tss.members) {if (tm.id === assignmentt) {assignment = tm; break;}}
await mem.send(`The secret santa has been started!\nYou've been assigned to **${assignment.name}**. They've written the following about what they want:\n\`${assignment.info}\`\n\nThe host has said this about when you should when you should have your gifts purchased and when you'll do gift exchange:\n\`${tss.end}\`\n\nThe following info is written on how much to spend on gifts: \n\`${tss.spend}\``)
.catch(async () => {
let host = await client.users.fetch(tss.owner);
await host.send(`There was a problem sending ${mem.name} their info. Please tell that member that they have been assigned to \`${assignment.name}\` and that they want \`${assignment.info}\`.`);
});
}
message.channel.send("**The secret santa has been started!** Everyone should have their assignments.");
}
}
};

@ -1,55 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const VC = require('../../models/vscount');
const makeId = require('../../util/makeid');
module.exports = {
name: "slap",
aliases: ['hit'],
help: "Use `{{p}}slap @person` to have me personally deliver your anger to them with a nice s l a p.",
meta: {
category: 'Fun',
description: "Slap another user! Virtually, of course.",
syntax: '`slap <@user>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'slap'}) ? await Saves.findOne({name: 'slap'}) : new Saves({name: 'slap'});
let saves = savess.saves;
if (!args.length) {
return message.channel.send(message.guild ? "Please mention someone to slap!" : "Oi! You don't get to waltz into my DM just to slap me!");}
if (mention && args[0].match(/^<@!?\d+>$/)) {
if (!message.guild) {return message.reply("Oi! You don't get to waltz into my DM just to slap me!");}
if (!message.guild.members.cache.has(mention.id)) {return message.reply("That user is not in this server!");}
if (message.author.id === mention.id) {return message.reply("Wait wouldn't slapping yourself be a form of self-harm? ToS is that you??");}
let slaps = await VC.findOne({uid: message.author.id, countOf: 'slap'}) || new VC({uid: message.author.id, countOf: 'slap'});
slaps.against[mention.id] = slaps.against[mention.id] ? slaps.against[mention.id] + 1 : 1;
slaps.total++;
slaps.markModified(`against.${mention.id}`);
slaps.save();
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: `${message.guild ? message.member.displayName : message.author.username} slaps ${message.guild.members.cache.get(mention.id).displayName}`, iconURL: message.author.displayAvatarURL()})
.setDescription(`That makes slap **#${slaps.against[mention.id]}** from you to them!`)
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('d93846')
.setFooter({text: `${slaps.total} slap${slaps.total === 1 ? '' : 's'} total`})
]});
}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staff) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Staff member in order to add new slap GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("Save added!");
}
}
};

@ -1,70 +0,0 @@
const Discord = require('discord.js');
const Chests = require("../../models/chests");
const ask = require('../../util/ask');
module.exports = {
name: "chests",
aliases: ['setupchests', 'enablechests', 'togglechests', 'tch'],
meta: {
category: 'Leveling',
description: "Enable or disable chests in your server, or set a channel for them to spawn in",
syntax: '`chests [enable|disable|toggle|channel]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Chests")
.setDescription("Enable or disable chests in your server, or set a channel for them to spawn in")
.addField("Notice", "You must have administrator permissions to edit these settings.")
.addField("Syntax", "`chests [enable|disable|channel]`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You must have administrator permissions in order to edit these settings.");}
if (!args.length) {args[0] = 'enable';}
if (['e', 'enable'].includes(args[0].toLowerCase())) {
if (client.misc.cache.chests.enabled.includes(message.guild.id)) {return message.channel.send("This server already has chest spawning enabled.")};
try {
am = await message.channel.send("Would you like to have me send chests to a specific channel?");
await am.react('👍');
await am.react('👎');
} catch {return message.channel.send(":thinking: hmmm... something went wrong there. I might not have permissions to add reactions to messages, and this could be the issue.");}
try {
let rc = am.createReactionCollector({filter: (r, u) => ['👍', '👎'].includes(r.emoji.name) && u.id === message.author.id, max: 1, time: 60000});
rc.on("collect", async r => {
useCh = r.emoji.name === "👍";
let chestCh = '';
if (useCh) {
let chestCh = await ask(message, 'What channel would you like me to send chests to? (Ideally, people should be able to speak in it so they can claim the chests)', 60000, false, true);
if (!chestCh) {return;}
if (!message.guild.channels.cache.has(chestCh) || !message.guild.channels.cache.has(chestCh.slice(2, chestCh.length - 1))) {return message.channel.send("That doesn't seem to be a channel! Try again?");}
if (chestCh.startsWith("<")) {chestCh = chestCh.slice(2, chestCh.length - 1);}
}
let c = new Chests({gid: message.guild.id, channel: chestCh});
c.save();
client.misc.cache.chests.enabled.push(message.guild.id);
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Chest Spawning Enabled!")
.setThumbnail(message.guild.iconURL({size: 2048}))
.setDescription(`Your server now has its chest spawning enabled! Chests will spawn in ${chestCh.length ? `<#${chestCh}>` : 'any channel'}.`)
.setColor("c375f0")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
});
rc.on("end", collected => {if (!collected.size) {return message.channel.send("Looks like you ran out of time! Try again?");}});
} catch {return message.channel.send("Hmm... there was some error problem thingy that happened when I tried to enable chest spawning for your server. If it keeps not working, then go yell at my devs!");}
}
else if (['d', 'disable'].includes(args[0].toLowerCase())) {
}
else if (['c', 'ch', 'setchannel', 'setch ', 'sc', 'sch', 'enable'].includes(args[0].toLowerCase())) {
}
else {return message.channel.send(`Invalid arg! Syntax: \`${prefix}chests [enable|disable|channel]\``);}
}
};

@ -1,29 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "claim",
aliases: [],
meta: {
category: 'Leveling',
description: "Claim a chest that has spawned in the channel",
syntax: '`claim [specialText]`',
extra: null,
guildOnly: true
},
help: "Claim a chest that has spawned in the channel. You must be in the same channel as the chest in order to claim it.",
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!client.misc.cache.chests.enabled.includes(message.guild.id)) {return message.channel.send("Chests aren't enabled in this server!");}
if (!client.misc.cache.chests.waiting.has(message.channel.id)) {return message.channel.send("There are no chests to claim in this channel.");}
if (!client.misc.cache.monners[message.author.id]) {return message.channel.send(`There was an issue on my side with claiming your chest. This happened because I don't have your ${message.misc.mn} info cached, so send a message anywhere and then try again. Sorry!`);}
let chest = client.misc.cache.chests.waiting.get(message.channel.id);
client.misc.cache.monners[message.author.id] += chest.amount;
client.misc.cache.chests.waiting.delete(message.channel.id);
chest.message.delete().catch(() => {});
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: message.member.displayName, iconURL: message.member.displayAvatarURL()})
.setDescription(`You've claimed ${client.utils.an(chest.rarity.name, true)} Chest with **${chest.amount} ${message.misc.mn}<:monners:926736756047495218>**`)
.setColor(chest.rarity.color)
]}).catch(() => {});
}
};

@ -1,52 +0,0 @@
const Discord = require('discord.js');
const moment = require('moment');
require('moment-precise-range-plugin');
const Monners = require('../../models/monners');
module.exports = {
name: "daily",
aliases: ['d'],
meta: {
category: 'Leveling',
description: "Claim some daily monners!",
syntax: '`daily`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Daily")
.setDescription("Claim some bonus monners once every 24 hours!")
.addField("Syntax", "`daily`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
let tm = await Monners.findOne({uid: message.author.id}) || new Monners({uid: message.author.id});
if (!client.misc.cache.monners[message.author.id]) {client.misc.cache.monners[message.author.id] = tm.currency;}
if (tm.daily && tm.daily.last && ((new Date().getTime() - tm.daily.last) < (1000 * 60 * 60 * 22))) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({iconURL: message.guild ? message.member.displayAvatarURL() : message.author.iconURL(), name: message.guild ? message.member.displayName : message.author.username})
.setDescription(`Your daily is not available yet! You can claim your next daily in **${moment.preciseDiff(new Date().getTime(), tm.daily.last + (1000 * 60 * 60 * 22))}**`)
.setColor('c92a45')
]});
}
if (!tm.daily || !Object.keys(tm.daily).length) {tm.daily = {total: 0, last: new Date().getTime(), streak: 0};}
let streakIncrease = false;
if (new Date().getTime() - tm.daily.last < (1000 * 60 * 60 * 47)) {
streakIncrease = true;
tm.daily.streak++
} else {tm.daily.streak = 1;}
tm.daily.total++;
tm.daily.last = new Date().getTime();
let bonus = (75 + (tm.daily.streak * (15 + Math.floor(Math.random() * 20))) + Math.floor(Math.random() * (15 + (tm.daily.streak * 2))));
client.misc.cache.monners[message.author.id] += bonus;
tm.markModified('daily');
['streak', 'last', 'total'].forEach(x => {tm.markModified(`daily.${x}`);});
tm.save();
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({iconURL: message.guild ? message.member.displayAvatarURL() : message.author.iconURL(), name: message.guild ? message.member.displayName : message.author.username})
.setDescription(`You've claimed today's daily!`)
.addField("Streak", streakIncrease ? `Your streak has **increased** to **${tm.daily.streak}**` : 'Your streak has **reset** to **1**.', true)
.addField(`Bonus ${message.misc.mn}`, `<:monners:926736756047495218> ${bonus}`, true)
.addField("Total Dailies Claimed", `${tm.daily.total}`)
.setColor('c375f0')
]});
}
};

@ -1,49 +0,0 @@
const Discord = require('discord.js');
const LXP = require('../../models/localxp');
const GuildData = require('../../models/guild');
module.exports = {
name: "ignorexp",
aliases: ['xpignore', 'noxp'],
meta: {
category: 'Leveling',
description: "Stop XP from being gained in a specific channel.",
syntax: '`ignorexp [#channel|channelId]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> AR Ignoring")
.setDescription("Provide a channel (or don't to use the current channel) to be voided from auto-responses, that way the responses won't send in places you don't want them to.")
.addField("Syntax", "`[#channel|channelId]` - channel is optional."),
async execute(message, msg, args, cmd, prefix, mention, client) {
const tg = await GuildData.findOne({gid: message.guild.id});
if ((!tg || !tg.staffrole || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You must have the staff role or be an administrator in this server in order to edit XP settings.");}
let txp = await LXP.findOne({gid: message.guild.id});
if (!txp) {return message.channel.send("This server doesn't have XP enabled.");}
let ch;
if (args[1]) {
ch = message.mentions.channels.first() || message.guild.channels.cache.get(args[1]);
if (!ch) {return message.channel.send("I couldn't find that channel. Please try again!");}
} else {ch = message.channel;}
ch = ch.id;
if (!txp.noGains) {txp.noGains = [];}
if (txp.noGains.includes(ch)) {
let ti = txp.noGains;
ti.splice(ti.indexOf(ch), 1);
txp.noGains = ti;
txp.markModified('noGains');
txp.save();
client.misc.cache.lxp.disabledChannels.set(message.guild.id, txp.noGains);
return message.channel.send("Got it. I'll start giving people XP here from now on.");
} else {
txp.noGains.push(ch);
txp.save();
client.misc.cache.lxp.disabledChannels.set(message.guild.id, txp.noGains);
return message.channel.send("I'll stop giving people XP in this channel from now on.");
}
}
};

@ -1,44 +0,0 @@
const Discord = require('discord.js');
const XP = require('../../models/localxp');
module.exports = {
name: "leaderboard",
aliases: ['lb', 'rank'],
meta: {
category: 'Leveling',
description: "Find your place in the server's ranks and see the top-ranking members in the server.",
syntax: '`leaderboard`',
extra: null,
guildOnly: true
},
help: "Find your place in the server's ranks and see the top-ranking members in the server.",
async execute(message, msg, args, cmd, prefix, mention, client) {
let cfmh = '<a:xp:870418598047387668>';
let gxp = await XP.findOne({gid: message.guild.id});
if (!gxp) {return message.channel.send("Your server doesn't have XP enabled! If it's something you want to use, you can enable it with the `setupleveling` command");}
let xp = gxp.xp;
let lvlp = Object.keys(xp).sort((a, b) => {return xp[a][1] - xp[b][1];}).reverse();
let lvl = lvlp.slice(0, Object.keys(xp).length >= 10 ? 10 : Object.keys(xp).length);
let lvls = ``;
let i; for (i=0; i<lvl.length; i++) {lvls += `${i+1}. <@${lvl[i]}> -> **Level ${xp[lvl[i]][1]}**\n`;}
lvls += `\n${cfmh} *You are ranked **#${lvlp.indexOf(message.author.id) + 1}** at Level ${xp[lvlp[lvlp.indexOf(message.author.id)]][1]}.*`;
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Server Leaderboard")
.setThumbnail(message.guild.iconURL({size: 2048, dynamic: true}))
.addField("Level", lvls)
.setColor('c375f0')
.setFooter({text: "Natsuki | Stats may be up to 2 minutes out of sync"})
.setTimestamp()
]});
/*u = Object.keys(tm.messages.members).sort((a, b) => {return tm.messages.members[a] - tm.messages.members[b];}).reverse().slice(0, Object.keys(tm.messages.members).length >= 5 ? 5 : Object.keys(tm.messages.members).length);
us = ``;
let i2; for (i2=0; i2<u.length; i2++) {us += `${i2+1}. <@${u[i2]}> -> **${tm.messages.members[u[i2]]} Messages**\n`;}*/
//if (args[0] && ['mooners', 'currency', 'balance', 'bal'].includes(args[0].toLowerCase())) {}
//else {}
}
};

@ -1,49 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
const LXP = require('../../models/localxp');
module.exports = {
name: "levelchannel",
aliases: ['lvch', 'lvlch', 'levelch', 'lvmsgch'],
meta: {
category: 'Leveling',
description: "Set the channel to send levelup messages to",
syntax: '`levelchannel <set|clear> [#channel]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Level Message Channel")
.setDescription("Specify a channel for me to send levelup messages to, or `clear` it to have me send the message in the same channel as the user.")
.addField("Syntax", "`levelchannel <set|clear> [#channel]`")
.addField("Notice", "You must be an administrator or have the specified staff role in your server to be able to use this command.")
.addField("See Also", "Looking for how to turn off level up messages in the server? Use `levelmessage`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}levelchannel <set|clear> [#channel]\``);}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staffrole || !tu.staffrole.length || !message.member.roles.cache.has(tu.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You don't have the permissions to do that in this server!");}
let xp = await LXP.findOne({gid: message.guild.id});
if (!xp) {return message.channel.send("Leveling isn't enabled in this server!");}
if (['s', 'set'].includes(args[0].toLowerCase())) {
args.shift();
if (!args.length) {return message.channel.send("Please try again and provide a channel to set the level up messages to be sent to.");}
let ch = message.mentions.channels.first() || message.guild.channels.cache.get(args[0]);
if (!ch) {return message.reply("I couldn't find that channel! Try again?");}
xp.lvch = ch.id;
xp.save();
return message.channel.send(`Got it! I'll send levelup messages to <#${ch.id}>`);
}
if (['c', 'clear'].includes(args[0].toLowerCase())) {
if (!xp.lvch.length) {return message.channel.send("I'm already not sending levelup messages to any specific channel!");}
xp.lvch = '';
xp.save();
return message.channel.send("Level up message channel cleared. I'll now send messages to the channel where the member levels up in.");
}
return message.channel.send("Invalid arg! Use `set` or `clear`.");
}
};

@ -1,98 +0,0 @@
const Discord = require('discord.js');
const GuildData = require('../../models/guild');
const LR = require('../../models/levelroles');
const ask = require('../../util/ask');
module.exports = {
name: "levelrole",
aliases: ['lr', 'levelroles', 'levelingroles', 'rolereward', 'rolerewards'],
meta: {
category: 'Leveling',
description: "Sets a role to be given to users when they reach a certain level.",
syntax: '`levelrole <set|view|remove|clear>`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Level Roles")
.setDescription("Sets a role to be given to users when they reach a certain level.")
.addField("Syntax", "`levelrole <set|view|remove|clear>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}levelrole <set|view|remove|clear>\``);}
let tg = await GuildData.findOne({gid: message.guild.id});
if ((!tg || !tg.staffrole || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You don't have the permissions to do that in this server! Ask a server admin to do it for you");}
if (['set', 's', 'add', 'a'].includes(args[0].toLowerCase())) {
if (!message.guild.me.permissions.has("MANAGE_ROLES")) {return message.channel.send("I don't have permissions to add roles to members in this server, so it would be useless to try and setup any level roles!");}
if (!args[1]) {return message.reply("please provide a level and a role to reward for reaching that level!");}
let level = args[1];
if (isNaN(Number(level)) || Number(level) > 200 || Number(level) < 1) {return message.reply("the level must be a positive number lower than 200!");}
if (!args[2]) {return message.channel.send("Please try again and provide a role mention or ID of the role you'd like to add after the level!");}
if (!args[2].match(/<@\&\d+>/gm) && !args[2].match(/\d+/gm)) {return message.channel.send("Hmm, it doesn't look like you gave me a role.");}
let role = message.mentions.roles.first() || message.guild.roles.cache.get(args[2]);
if (!role) {return message.channel.send("I can't find that role!");}
role = role.id;
let lr = await LR.findOne({gid: message.guild.id}) || new LR({gid: message.guild.id});
if (Object.keys(lr.roles).length >= 20) {return message.channel.send("Due to data storage concerns, you can only have 20 level roles in this server. If you believe you need more, come to the support server and talk to my devs and see if they would be willing to raise this requirement for you.");}
lr.roles[level] = role;
lr.markModified(`roles.${level}`);
lr.save();
if (!client.misc.cache.lxp.hasLevelRoles.includes(message.guild.id)) {client.misc.cache.lxp.hasLevelRoles.push(message.guild.id);}
return message.channel.send(`Got it, I'll now give members the role \`${message.guild.roles.cache.get(role).name}\` when they reach Level ${level}`);
}
if (['v', 'view', 'l', 'list'].includes(args[0].toLowerCase())) {
let lr = await LR.findOne({gid: message.guild.id});
if (!lr) {return message.channel.send("Your server doesn't seem to have any leveling roles set up!");}
let s = '';
let rs = [];
let r; for (r of Object.keys(lr.roles)) {
let role = message.guild.roles.cache.get(lr.roles[r]);
if (role) {rs.push({level: r, role: role});}
else {
role = await message.guild.roles.fetch(lr.roles[r]);
if (role) {rs.push({level: r, role: role});}
}
}
rs.sort((a, b) => a.level - b.level);
for (let i = 0; i < rs.length; i++) {s += `**${i + 1}.** Level ${rs[i].level} - <@&${rs[i].role.id}>\n`;}
if (!s.length) {return message.channel.send("Hmm, there was some kind of error there. It may be that your server's leveling roles were deleted, or there was some internal error when trying to read them. Contact my devs if the problem persists.");}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Server Leveling Roles")
.setThumbnail(message.guild.iconURL({size: 2048}))
.setDescription(s)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
if (['d', 'delete', 'r', 'remove'].includes(args[0].toLowerCase())) {
let lr = await LR.findOne({gid: message.guild.id});
if (!lr) {return message.channel.send("Your server doesn't seem to have any leveling roles set up!");}
if (!args[1]) {return message.channel.send("Please provide the level you'd like to remove from the level roles *not the role you want to remove*");}
if (!lr.roles[args[1]]) {return message.channel.send("Hmm, it looks like that level doesn't have a role for it. Make sure you provided the *level* and not the *role*.");}
delete lr.roles[args[1]];
lr.markModified(`roles.${args[1]}`);
lr.save();
return message.channel.send("Removed that leveling role!");
}
if (['c', 'clear'].includes(args[0].toLowerCase())) {
if (!message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("Unfortunately, you must be an administrator in this server to clear all the leveling roles.");}
let lr = await LR.findOne({gid: message.guild.id});
if (!lr) {return message.channel.send("Your server doesn't seem to have any leveling roles set up!");}
let conf = await ask(message, "Are you sure you want to clear your server's leveling roles? This is irreversible! (Accepts only \"yes\" or \"no\")"); if (!conf) {return;}
if (conf.toLowerCase() !== "yes") {return message.channel.send("Fear not! Your leveling roles are safe, I will still be giving roles to people when they level up.");}
return LR.deleteOne({gid: message.guild.id}).then(() => {
return message.channel.send("Leveling roles cleared successfully!");
}).catch(() => {
return message.channel.send("There was some kind of issue in deleting your server's leveling roles. Contact my devs if the problem persists!");
});
}
return message.channel.send(`Invalid arg! Syntax: \`${prefix}levelrole <set|view|remove|clear>\``);
}
};

@ -1,41 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
module.exports = {
name: "namemonners",
aliases: ['namemon', 'nm'],
meta: {
category: 'Leveling',
description: "Rename the currency for your server",
syntax: '`namemonners <clear|name>`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Monners Naming")
.setDescription("Rename the Monners for your server. This merely a cosmetic effect. Monners gains will remain global regardless.")
.addField("Notice", "You must be an administrator in the server in order to edit these settings.")
.addField("Syntax", "`namemonners <clear|name>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}namemonners <clear|name>\``);}
if (!message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You must be an administrator in this server in order to do that.");}
let name = args.join(" ");
const names = await Saves.findOne({name: 'monnersnames'});
const nsaves = names.saves;
if (name.toLowerCase() === 'clear') {
nsaves.delete(message.guild.id);
client.misc.cache.monnersNames.delete(message.guild.id);
message.channel.send("I'll now refer to currency in this server as Monners again.");
} else {
if (name.length > 12) {return message.channel.send("That name is too long! Keep it short and simple.");}
if (name.match(/<a?:.+:\d+>/gm)) {return message.channel.send("You can't have an emoji in your monners name.");}
if (!name.match(/^[a-zA-Z0-9-]+$/gm)) {return message.channel.send("Your name must contain only alphanumeric characters.");}
nsaves.set(message.guild.id, name);
client.misc.cache.monnersNames.set(message.guild.id, name);
message.channel.send(`Cool. I'll refer to Monners in this server as "${name}" now.`);
}
names.saves = nsaves;
return await names.save();
}
};

@ -1,48 +0,0 @@
const Discord = require('discord.js');
const LXP = require('../../models/localxp');
module.exports = {
name: "setupleveling",
aliases: ['setuplvl', 'setlvl', 'setleveling', 'levelingsetup', 'lvlset', 'lvlsetup'],
meta: {
category: 'Leveling',
description: "Setup and enable your server's local leveling!",
syntax: '`setupleveling`',
extra: "Requires administrator permissions. Disabled by default.",
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Local Leveling Setup")
.setDescription("Set up your local leveling system to allow your server's members to progress in ranks, which lets you enable leveling roles")
.addField("Syntax", "`setupleveling`")
.addField("Important Notice", "You must be a server administrator in order to use this command. Please know that local leveling systems can cost a great deal of our database storage space when used on larger servers, so please only enable this feature **if you will actually make use of it**, not just for fun."),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You must be a **server administrator** in order to use this command.");}
if (await LXP.findOne({gid: message.guild.id})) {return message.channel.send("Your leveling is already set up!");} //TODO add message to lead to disable cmd when complete
let am;
try {
am = await message.channel.send("Would you like to enable :thumbsup: or disable :thumbsdown: level up messages? (You can specify a set channel for this later.)");
await am.react('👍');
await am.react('👎');
} catch {return message.channel.send(":thinking: hmmm... something went wrong there. I might not have permissions to add reactions to messages, and this could be the issue.");}
try {
let rc = am.createReactionCollector({filter: (r, u) => ['👍', '👎'].includes(r.emoji.name) && u.id === message.author.id, max: 1, time: 60000});
rc.on("collect", async r => {
let xp = new LXP({gid: message.guild.id});
xp.msg = r.emoji.name === "👍";
xp.save();
client.misc.cache.lxp.enabled.push(message.guild.id);
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("XP System Enabled!")
.setThumbnail(message.guild.iconURL({size: 2048}))
.setDescription(`Your server now has its leveling system enabled! If you enabled level up messages, you can set the channel for that using \`${prefix}levelchannel\`.\n\nAll members of your server will now gain XP as they talk. I'm a smart cookie, so don't try spamming. It won't work. Every member can gain more XP once per minute, no matter how many messages they send in that one minute.\n\nIf you want to see your level and how much more XP you need to level up, run \`${prefix}stats\`.`)
.setColor("c375f0")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
});
rc.on("end", collected => {if (!collected.size) {return message.channel.send("Looks like you ran out of time! Try again?");}});
} catch {return message.channel.send("Hmm... there was some error problem thingy that happened when I tried to enable XP for your server. If it keeps not working, then go yell at my devs!");}
}
};

@ -1,122 +0,0 @@
const Discord = require('discord.js');
const Canvas = require('canvas');
Canvas.registerFont('./resources/fonts/Nunito-Regular.ttf', {family: "Nunito"});
const Monners = require('../../models/monners');
const applyText = (base, canvas, text) => {
const ctx = canvas.getContext('2d');
let fontSize = base;
do {ctx.font = `${fontSize -= 2}px "Nunito"`;}
while (ctx.measureText(text).width > canvas.width - 460);
return ctx.font;
};
function roundRect(ctx, x, y, width, height, radius=5, fill=false, stroke=true, clip=false) {
if (typeof radius === 'number') {radius = {tl: radius, tr: radius, br: radius, bl: radius};
} else {
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
for (let side in defaultRadius) {radius[side] = radius[side] || defaultRadius[side];}
}
ctx.beginPath();
ctx.moveTo(x + radius.tl, y);
ctx.lineTo(x + width - radius.tr, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
ctx.lineTo(x + width, y + height - radius.br);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
ctx.lineTo(x + radius.bl, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
ctx.lineTo(x, y + radius.tl);
ctx.quadraticCurveTo(x, y, x + radius.tl, y);
ctx.closePath();
if (fill) {ctx.fill();}
if (stroke) {ctx.stroke();}
if (clip) {ctx.clip();}
}
const LXP = require('../../models/localxp');
module.exports = {
name: "stats",
aliases: ['level', 'xp', 'lvl'],
meta: {
category: 'Leveling',
description: "View your rank in the server",
syntax: '`stats [@user|userID]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Stats")
.setDescription("View your level and XP in the server, or someone else's")
.addField("Syntax", "`stats [@user|userID]`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
let u = args[0] ? (message.mentions.members.first() || message.guild.members.cache.get(args[0])) : message.member;
if (!u) {return message.channel.send("I can't find that user!");}
let xp; let hasXp = true;
if (!client.misc.cache.lxp.xp[message.guild.id] || !client.misc.cache.lxp.xp[message.guild.id][u.id]) {
let txp = await LXP.findOne({gid: message.guild.id});
if (!txp) {hasXp = false;}
else {if (txp.xp[u.id]) {xp = {xp: txp.xp[u.id][0], level: txp.xp[u.id][1]};}}
} else if (hasXp) {xp = client.misc.cache.lxp.xp[message.guild.id][u.id];}
let tmoon = client.misc.cache.monners[u.id] ? {currency: client.misc.cache.monners[u.id]} : await Monners.findOne({uid: u.id});
if (!tmoon) {return message.channel.send("There was an error getting your level and Monners information. Try again?");}
let tcur = tmoon ? tmoon.currency : 0;
if (!message.channel.permissionsFor(message.guild.me.id).has("ATTACH_FILES")) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`${u.displayName}${u.displayName.toLowerCase().endsWith('s') ? "'" : "'s"} Stats`)
.setDescription("Local leveling stats")
.addField("Level", `${xp.level}`, true)
.addField("XP", `**${xp.xp}** of **${Math.ceil(100 + (((xp.level / 3) ** 2) * 2))}** needed to level up`, true)
.addField(`${message.misc.mn}`, `<:monners:926736756047495218> ${tcur}`)
.setThumbnail(client.users.cache.get(u.id).displayAvatarURL({size: 2048}))
.setColor("c375f0")
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
} else {
const canvas = Canvas.createCanvas(1193, 411);
const ctx = canvas.getContext('2d');
const background = await Canvas.loadImage('./resources/images/nat-lvl.jpg');
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#13131337'; //darken img
ctx.fillRect(0, 0, canvas.width, canvas.height);
const avatar = await Canvas.loadImage(u.displayAvatarURL({format: 'jpg', size: 2048}));
ctx.drawImage(avatar, 40, 40, canvas.height - 80, canvas.height - 80); //draw avatar
ctx.font = applyText(50, canvas, `${u.displayName}${u.displayName.toLowerCase().endsWith('s') ? "'" : "'s"} Stats`); //center text
ctx.fillStyle = '#ffffff';
ctx.fillText(`${u.displayName}${u.displayName.toLowerCase().endsWith('s') ? "'" : "'s"} Stats`, canvas.width / 2.8, canvas.height / 2);
ctx.font = applyText(120, canvas, xp ? `${xp.xp} / ${Math.ceil(100 + (((xp.level / 3) ** 2) * 2))} | Level ${xp.level}` : "Leveling N/A"); //top text
ctx.fillStyle = '#ffffff';
ctx.fillText(xp ? `${xp.xp} / ${Math.ceil(100 + (((xp.level / 3) ** 2) * 2))} | Level ${xp.level}` : "Leveling N/A", canvas.width / 2.8, canvas.height / 3.2);
const monnersImage = await Canvas.loadImage('https://cdn.discordapp.com/emojis/926736756047495218');
ctx.drawImage(monnersImage, canvas.width / 2.8, (canvas.height / 1.53) - 11, 58, 60); //draw monners icon
ctx.font = `50px "Nunito"`;
ctx.fillText(`${tcur}`, (canvas.width / 2.8) + 70, (canvas.height / 1.53) + 57 - 20);
ctx.fillText(` | `, (canvas.width / 2.8) + 70 + ctx.measureText(`${tcur}`).width, (canvas.height / 1.53) + 57 - 24); // draw monners amount
let monnersWidth = ctx.measureText(`${tcur} | `).width + 75; //get width of monners text and icon to account for bar size later
if (xp) {
//draw the bar borders
ctx.strokeStyle = '#ffffff';
ctx.strokeWidth = 6;
roundRect(ctx, (canvas.width / 2.8) + monnersWidth, canvas.height / 1.53, canvas.width - (canvas.width / 2.8) - monnersWidth - 80, 40, 10, false, true, false);
//set a clipping area to keep the bar filler inside the rounded borders
roundRect(ctx, (canvas.width / 2.8) + monnersWidth, canvas.height / 1.53, canvas.width - (canvas.width / 2.8) - monnersWidth - 80, 40, 10, false, false, true);
ctx.fillStyle = '#4aa4e0c8';
//draw the bar filler
ctx.fillRect((canvas.width / 2.8) + monnersWidth, canvas.height / 1.53, (xp.xp / Math.ceil(100 + (((xp.level / 3) ** 2) * 2))) * (canvas.width - (canvas.width / 2.8) - monnersWidth - 80), 40);
}
message.channel.send({files: [new Discord.MessageAttachment(canvas.toBuffer(), 'xp-stats.png')]});
}
}
};

@ -1,26 +0,0 @@
const Discord = require('discord.js');
const Monners = require("../../models/monners");
module.exports = {
name: "streak",
aliases: [],
meta: {
category: 'Leveling',
description: "View yours or someone else's daily streak.",
syntax: '`streak [@user]`',
extra: null
},
help: "View your streak, or mention someone else to view theirs.",
async execute(message, msg, args, cmd, prefix, mention, client) {
const tm = await Monners.findOne({uid: mention ? mention.id : message.author.id});
return message.channel.send(tm ? {embeds: [
new Discord.MessageEmbed()
.setAuthor({name: "Daily Streak", iconURL: mention ? mention.displayAvatarURL() : message.author.displayAvatarURL()})
.setDescription(`${mention ? "That user's" : "Your"} streak is **${tm.daily.streak}**.`)
.setFooter({text: "Natsuki"})
.setColor("c375f0")
.setTimestamp()
]} : `${mention ? "That user doesn't" : "You don't"} have a streak yet!`);
}
};

@ -1,203 +0,0 @@
const Discord = require('discord.js');
const AR = require('../../models/ar');
const GuildData = require('../../models/guild');
const ask = require('../../util/ask');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "ar",
aliases: ['autoresponse', 'autoresponses'],
meta: {
category: 'Misc',
description: "Create and edit automatic responses, which lets the bot say stuff when you say something in your server!",
syntax: '`ar <add|edit|delete|settings|list>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Auto Responses")
.setDescription("Create and edit automatic responses, which lets the bot say stuff when you say something in your server!")
.addField("Syntax", "`ar <add|edit|delete|settings|list>`")
.addField("Notice", "This command is server-only, and requires you to be an administrator or have the staff role."),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.channel.send("You must be in a server in order to use this command.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}ar <add|edit|delete|settings|list>\``);}
const tg = await GuildData.findOne({gid: message.guild.id});
if (['a', 'add', 'e', 'edit', 'delete', 'd', 's', 'settings'].includes(args[0].toLowerCase()) && ((!tg || !tg.staffrole || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR"))) {return message.channel.send("You must have the staff role or be an administrator in this server in order to edit AR settings.");}
function sortARs(tar) {
let t = tar.triggers;
let res = {};
res.paginate = t.length > 10;
let ar = tar.ars;
if (res.paginate) {
let pages = [];
let cond = false;
let x = 0;
while (true) {
let s = '';
for (let i = 0; i < 10; i++) {
if (ar[(x * 10) + i] === undefined) {cond = true; break;}
s += `\`${(x*10)+i+1}.\` ${t[(x * 10) + i]}\n-> ${ar[(x * 10) + i]}\n\n`;
if ((x * 10) + i >= ar.length) {cond = true; break;}
}
pages.push(new Discord.MessageEmbed()
.setTitle(`Auto-Responses in this Server`)
.setDescription(s)
.setColor('c375f0')
.setTimestamp()
);
if (cond) {break;}
x++;
}
res.pagination = new Pagination(message.channel, pages, message, client, true);
} else {
let s = '';
for (let i=0;i<t.length;i++) {s+=`\`${i+1}.\` ${t[i]}\n-> ${ar[i]}\n\n`;}
res.s = s;
}
return res;
}
function viewARs(res, mode) {
return new Promise(async resolve => {
if (res.paginate) {
if (mode) {res.pagination.pages.forEach(page => page.addField(mode === 'edit' ? "Editing" : 'Deletion', `Please say the **number** of the AR you wish to ${mode}.`));}
let r = await res.pagination.start({endTime: 60000, user: message.author.id});
return resolve(r);
} else {
let string = res.s;
let embed = new Discord.MessageEmbed()
.setTitle("Auto-Responses in this Server")
.setDescription(string)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
if (mode) {embed.addField(mode === 'edit' ? "Editing" : 'Deletion', `Please say the **number** of the AR you wish to ${mode}.`);}
let r = await message.channel.send({embeds: [embed]});
return resolve(r);
}
});
}
if (['a', 'add'].includes(args[0].toLowerCase())) {
let trigger;
if (args[1]) {
let targs = args.slice(0);
targs.shift();
trigger = targs.join(' ');
} else {trigger = await ask(message, "What would you like the trigger to be? This is the message that will make your AR work.", 120000); if (!trigger) {return;}}
if (`${trigger}`.length > 150) {return message.channel.send("Your trigger needs to be less than 150 characters, please!");}
let response = await ask(message, "What would you like my response to be?", 120000); if (!response) {return;}
if (`${response}`.length > 300) {return message.channel.send("Your response needs to be less than 300 characters, please!");}
let tar = await AR.findOne({gid: message.guild.id}) || new AR({gid: message.guild.id});
if (tar.triggers.length === 20) {return message.channel.send("Because of data storage concerns, your ARs are capped at 20 per server. You can join the official support server and talk to the devs if you have a legitimate reason for raising this limit and they can see about raising it for you!");}
let h = false; let ar; for (ar of tar.triggers) {if (ar && ar.toLowerCase() === `${trigger}`.toLowerCase()) {h = true;}}
if (h) {return message.channel.send("You seem to already have that trigger. Try using `edit` instead!");}
tar.triggers.push(trigger.toLowerCase());
client.misc.cache.ar.set(message.guild.id, tar.triggers);
tar.ars.push(response);
tar.save();
return message.channel.send("AR added!");
}
if (['e', 'edit'].includes(args[0].toLowerCase())) {
let tar = await AR.findOne({gid: message.guild.id});
if (!tar || !tar.triggers.length) {return message.channel.send("You can't edit any auto-responses... because there aren't any here...");}
let sar = sortARs(tar);
await viewARs(sar, 'edit');
let collected;
try {collected = await message.channel.awaitMessages({filter: m => m.author.id === message.author.id, errors: ['time'], time: 60001, max: 1});}
catch {return message.channel.send("This question has timed out. Please try again!");}
collected = collected.first().content.trim();
if (isNaN(Number(collected))) {return message.channel.send("Hmmm, maybe try replying with a *number*!");}
let id = Number(collected);
if (id < 1 || id > tar.triggers.length) {return message.channel.send("Your number was either below 1 or doesn't have a trigger to match it.");}
try {
let response = await ask(message, "What would you like the new response to be?", 120000); if (!response) {return;}
if (`${response}`.length > 300) {return message.channel.send("Your response needs to be less than 300 characters, please!");}
let tarars = tar.ars;
tarars[id-1] = response;
tar.ars = tarars;
tar.markModified('ars');
tar.save();
return message.channel.send("Yeah, that response seems to fit better than the last one.");
} catch {return message.channel.send("There seemed to have been a problem deleting that AR. Contact my devs if the problem persists.");}
}
if (['d', 'delete'].includes(args[0].toLowerCase())) {
let tar = await AR.findOne({gid: message.guild.id});
if (!tar || !tar.triggers.length) {return message.channel.send("It's not like this server has any ARs for me to delete in the first place!");}
let sar = sortARs(tar);
await viewARs(sar, 'delete');
let collected;
try {collected = await message.channel.awaitMessages({filter: m => m.author.id === message.author.id, errors: ['time'], time: 60000, max: 1});}
catch {return message.channel.send("This question has timed out. Please try again!");}
collected = collected.first().content.trim();
if (isNaN(Number(collected))) {return message.channel.send("You didn't reply with a number!");}
let id = Number(collected);
if (id < 1 || id > tar.triggers.length) {return message.channel.send("Your number was either below 1 or doesn't have a trigger to match it.");}
try {
let temptt = tar.triggers;
temptt.splice(id-1, 1);
let tempar = tar.ars;
tempar.splice(id-1, 1);
tar.triggers = temptt; tar.ars = tempar;
tar.markModified('triggers'); tar.markModified('ars');
tar.save();
client.misc.cache.ar.set(message.guild.id, tar.triggers);
return message.channel.send("I didn't like saying that anyway.");
} catch {return message.channel.send("There seemed to have been a problem deleting that AR. Contact my devs if the problem persists.");}
}
if (['v', 'view', 'l', 'list'].includes(args[0].toLowerCase())) {
let tar = await AR.findOne({gid: message.guild.id});
if (!tar || !tar.triggers.length) {return message.channel.send("This server has no ARs!");}
return viewARs(sortARs(tar));
}
if (['s', 'settings'].includes(args[0].toLowerCase())) {
args.shift();
if (!args.length) {return message.reply("You can `view` your server's AR settings or `ignore` a channel");}
let tar = await AR.findOne({gid: message.guild.id});
if (!tar || !tar.triggers.length) {return message.channel.send("This server doesn't have any auto-responses, so changing or viewing the settings would be pointless...");}
if (['v', 'view'].includes(args[0].toLowerCase())) {
if (!tar.ignoreChs.length) {return message.channel.send("This server doesn't have any channels set to be ignored for ARs.");}
let s = '';
let c; for (c of tar.ignoreChs) {s += `<#${c}>, `}
return message.channel.send(`Channels I won't do auto-responses in: ${s.slice(0, s.length - 2)}`);
}
if (['i', 'ignore'].includes(args[0].toLowerCase())) {
let ch;
if (args[1]) {
ch = message.mentions.channels.first() || message.guild.channels.cache.get(args[1]);
if (!ch) {return message.channel.send("I couldn't find that channel. Please try again!");}
} else {ch = message.channel;}
ch = ch.id;
if (tar.ignoreChs.includes(ch)) {
let ti = tar.ignoreChs;
ti.splice(ti.indexOf(ch), 1);
tar.ignoreChs = ti;
tar.markModified('ignoreChs');
tar.save();
client.misc.cache.arIgnore.set(message.guild.id, tar.ignoreChs);
return message.channel.send("I'll start replying to Auto Responses in this channel from now on.");
} else {
tar.ignoreChs.push(ch);
tar.save();
client.misc.cache.arIgnore.set(message.guild.id, tar.ignoreChs);
return message.channel.send("Got it. I'll ignore Auto Responses here from now on.");
}
}
}
return message.channel.send(`That's not a valid argument! Try \`${prefix}help ar\``);
}
};

@ -1,52 +0,0 @@
const Discord = require('discord.js');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "avatar",
aliases: ['av', 'a', 'pfp'],
help: "Use `{{p}}avatar` to get your own profile picture, or mention someone to get theirs!",
meta: {
category: 'Misc',
description: "Flare your avatar or peek at others'",
syntax: '`avatar [@mention]`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let fail = false;
let member = args.length && !fail ? (mention || client.users.cache.get(args[0]) || message.author) : message.author;
await client.users.fetch(member.id, {force: true}).catch(() => fail = true);
if (message.guild) {message.guild.members.fetch(member.id, {force: true});}
let name = message.guild ? message.guild.members.cache.get(member.id).displayName : member.username;
let options = new TagFilter([
new Tag(['small', 's', 'mini', 'm'], 'small', 'toggle'),
new Tag(['verysmall', 'vsmall', '-vs', 'xs'], 'vsmall', 'toggle'),
new Tag(['g', 'global', 'user', 'u'], 'global', 'toggle'),
new Tag(['b', 'both'], 'both', 'toggle')
]).test(args.join(" "));
try {
if (options.both && message.guild ? member.displayAvatarURL({size: 2048, dynamic: true, format: 'png'}) !== message.guild.members.cache.get(member.id).displayAvatarURL({size: 2048, dynamic: true, format: 'png'}) : false) {
const pag = new Pagination(message.channel, [message.guild.members, client.users]
.map(source => source.cache.get(member.id).displayAvatarURL({size: options.vsmall ? 128 : options.small ? 256 : 2048, dynamic: true, format: "png"}))
.map((avatar, index) => new Discord.MessageEmbed()
.setTitle(`${name.endsWith('s') ? `${name}'` : `${name}'s`} ${['Server', 'Global'][index]} Avatar`)
.setImage(avatar)
.setColor('c375f0')
), message, client
);
return await pag.start({user: message.author.id, time: 60000});
} else {
let avem = new Discord.MessageEmbed()
.setTitle(`${name.endsWith('s') ? `${name}'` : `${name}'s`} Avatar`)
.setImage(message.guild ? (options.global ? client.users : message.guild.members).cache.get(member.id).displayAvatarURL({size: options.vsmall ? 128 : options.small ? 256 : 2048, dynamic: true, format: "png"}) : member.displayAvatarURL({size: options.vsmall ? 128 : options.small ? 256 : 2048, dynamic: true, format: "png"}))
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
if (!options.vsmall) {avem.setTimestamp();}
return message.channel.send({embeds: [avem]});
}
} catch (e) {console.error(e); return message.reply("Hmm, there seems to have been an error while I tried to show you that user's avatar.");}
}
};

@ -1,25 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "commands",
aliases: ['cmds', 'commandlist', 'cmdlist'],
meta: {
category: 'Misc',
description: "Shows a list of my commands",
syntax: '`commands`',
extra: null
},
help: "Shows a list of all my commands",
async execute(message, msg, args, cmd, prefix, mention, client) {
let categories = [];
Array.from(client.commands.values()).forEach(c => {if (!categories.includes(c.meta ? c.meta.category : 'Uncategorized')) {categories.push(c.meta ? c.meta.category : 'Uncategorized');}});
let ce = new Discord.MessageEmbed()
.setTitle("Commands")
.setDescription(`You can use \`${prefix}help\` on any command to get more help on it.`)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
categories.forEach(category => ce.addField(category, Array.from(client.commands.values()).filter(command => command.meta ? command.meta.category === category : category === "Uncategorized").map(cmd => `\`${cmd.name}\``).join(', ')));
return message.channel.send({embeds: [ce]});
}
};

@ -1,122 +0,0 @@
const Discord = require('discord.js');
const {Pagination} = require('../../util/pagination');
module.exports = {
name: "emoji",
aliases: ['emote', 'emojiinfo', 'emoteinfo', 'ei', 'e'],
meta: {
category: 'Misc',
description: "Get info on a certain emoji",
syntax: '`emoji <:emoji:|emojiID>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Emoji")
.setDescription("Get info on an emoji, such as its ID, name, and a link to the source image. You can also use this for the `robemoji` command to add an emoji by its ID.")
.addField("Syntax", "`emoji <:emoji:|emojiID>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {
if (!message.guild.id) {return message.channel.send(`Syntax: \`${prefix}emoji <:emoji:|emojiID>\``);}
let emotes = [];
Array.from(message.channel.messages.cache.values()).forEach(m => {
let margs = m.content.split(/\s+/gm);
margs.forEach(marg => {if (marg.match(/^<a?:.+:\d+>$/) && !emotes.includes(marg)) {emotes.push(marg);}});
if (m.reactions.cache.size) {Array.from(m.reactions.cache.values()).forEach(r => {if (r.emoji.id && !emotes.includes(`<${r.emoji.animated ? 'a' : ''}:${r.emoji.name}:${r.emoji.id}>`)) {emotes.push(`<${r.emoji.animated ? 'a' : ''}:${r.emoji.name}:${r.emoji.id}>`);}});}
});
if (!emotes.length) {return message.channel.send("It doesn't look like anyone has sent any emoji recently. Try using the command again but adding an emoji to the message to get info on it.");}
emotes.reverse();
let emoteEmbeds = [];
emotes.forEach(emote => {
let spl = emote.split(':');
let finEm = new Discord.MessageEmbed()
.setTitle("Emoji Info")
.setDescription(`Name: ${spl[1] ? `\`:${spl[1]}:\`` : "Not Found"}\nID: ${spl[2].slice(0, spl[2].length - 1)}\nURL: [Here](${`https://cdn.discordapp.com/emojis/${spl[2].slice(0, spl[2].length - 1)}${spl[0].includes('a') ? '.gif' : ""}`})\nAnimated: ${spl[0].includes('a') === true}\n\nI have access: ${client.emojis.cache.has(spl[2].slice(0, spl[2].length - 1))}`)
.setColor('c375f0')
.setImage(`https://cdn.discordapp.com/emojis/${spl[2].slice(0, spl[2].length - 1)}${spl[0].includes('a') ? '.gif' : ""}`)
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
if (client.emojis.cache.has(spl[2].slice(0, spl[2].length - 1))) {finEm.setThumbnail(client.emojis.cache.get(spl[2].slice(0, spl[2].length - 1)).guild.iconURL({size: 1024}));}
if (client.emojis.cache.has(spl[2].slice(0, spl[2].length - 1)) && client.emojis.cache.get(spl[2].slice(0, spl[2].length - 1)).guild.members.cache.has(message.author.id) && client.emojis.cache.get(spl[2].slice(0, spl[2].length - 1)).guild.id !== (message.guild ? message.guild.id : 1)) {finEm.addField("Server", `You're in the server this emoji is from: **${client.emojis.cache.get(spl[2].slice(0, spl[2].length - 1)).guild.name}**`);}
emoteEmbeds.push(finEm);
});
let emojiList = new Pagination(message.channel, emoteEmbeds, message, client, true);
return emojiList.start({user: message.author.id, endTime: 60000});
}
if (!args[0].match(/^<a?:.+:\d+>$|^\d+$/gm)) {
if (args[0].length > 25) {return message.channel.send("That doesn't seem to be a valid emoji! (Standard Discord emojis don't count :p )");}
let lookup = client.emojis.cache.filter(e => (args[0].length > 2 && e.name.toLowerCase().includes(args[0].toLowerCase())) || e.name.toLowerCase() === args[0].toLowerCase());
if (!lookup.size) {return message.channel.send("I tried to find some emojis that matched that name, but couldn't find anything. Maybe you didn't give an emoji to begin with?");}
let emotes = Array.from(lookup.values());
if (lookup.size > 20) {
let pages = [];
let x = 0;
while (true) {
let cond = false;
let page = '';
for (let i = 0; i < 20; i++) {
if (emotes[(x * 20) + i] === undefined) {cond = true; break;}
page += `<${emotes[(x * 20) + i].animated ? 'a' : ''}:${emotes[(x * 20) + i].name}:${emotes[(x * 20) + i].id}> \`:${emotes[(x * 20) + i].name}:\` -> ${emotes[(x * 20) + i].id}\n`;
if ((x * 20) + i >= emotes.length) {cond = true; break;}
}
pages.push(new Discord.MessageEmbed()
.setTitle(`Emoji Lookup Results [${(x * 20) + 1}-${(x * 20) + 20} of ${lookup.size}]`)
.setDescription(page)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
);
if (cond) {break;}
x++;
}
let emojiList = new Pagination(message.channel, pages, message, client, true);
return emojiList.start({user: message.author.id, endTime: 60000});
} else {
let page = '';
for (let i = 0; i < lookup.size; i++) {page += `<${emotes[i].animated ? 'a' : ''}:${emotes[i].name}:${emotes[i].id}> \`:${emotes[i].name}:\` -> ${emotes[i].id}\n`;}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`Emoji Lookup Results - ${lookup.size}`)
.setDescription(page)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
}
let name; let id; let animated; let url;
let access = false;
if (args[0].match(/^<a?:.+:\d+>$/)) {
let spl = args[0].split(':');
name = spl[1];
id = spl[2].slice(0, spl[2].length - 1);
animated = spl[0].includes('a');
url = `https://cdn.discordapp.com/emojis/${id}`;
access = client.emojis.cache.has(id);
} else {
id = args[0];
url = `https://cdn.discordapp.com/emojis/${id}`;
access = client.emojis.cache.has(id);
if (access) {
name = client.emojis.cache.get(id).name;
animated = client.emojis.cache.get(id).animated;
}
}
if (animated) {url += '.gif';}
try {
let finEm = new Discord.MessageEmbed()
.setTitle("Emoji Info")
.setDescription(`Name: ${name ? `\`:${name}:\`` : "Not Found"}\nID: ${id}\nURL: [Here](${url})\nAnimated: ${animated === true}\n\nI have access: ${access}`)
.setColor('c375f0')
.setImage(url)
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
if (access) {finEm.setThumbnail(client.emojis.cache.get(id).guild.iconURL({size: 1024}));}
if (access && client.emojis.cache.get(id).guild.members.cache.has(message.author.id) && client.emojis.cache.get(id).guild.id !== (message.guild ? message.guild.id : 1)) {finEm.addField("Server", `You're in the server this emoji is from: **${client.emojis.cache.get(id).guild.name}**`);}
return message.channel.send({embeds: [finEm]});
} catch {
return message.channel.send("There was an error getting info for that emoji. You may not have given a valid emoji, or the ID you gave doesn't lead to a real emoji.");
}
}
};

@ -1,118 +0,0 @@
const Discord = require("discord.js");
const {Pagination} = require('../../util/pagination');
const ask = require('../../util/ask');
module.exports = {
name: "help",
aliases: ["h", "commands"],
help: 'you silly! What did you expect me to respond with?',
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {
let sorted = {};
await Array.from(client.commands.values()).forEach(command => {if (command.name !== "help" && command.meta) {
sorted[command.meta.category] = sorted[command.meta.category] ? sorted[command.meta.category] : {};
sorted[command.meta.category][command.name] = command;
}});
let helpSorted = {};
let category; for (category of Object.keys(sorted)) {
let categorySorted = [];
let current = 1;
let currentEmbed = new Discord.MessageEmbed().setAuthor({name: "Help Menu", iconURL: message.author.displayAvatarURL()}).setTitle(category).setDescription("React to control the menu! You can also specify a command name when doing the help command to get more info about it.").setColor("c375f0");
let commands = Object.keys(sorted[category]);
let command; for (command of commands) {
let aliases = '';
let a; if (sorted[category][command].aliases) {for (a of sorted[category][command].aliases) {aliases += `\`${a}\`, `}}
aliases = aliases.length ? aliases.slice(0, aliases.length - 2) : 'None';
currentEmbed.addField(`${command.slice(0,1).toUpperCase()}${command.slice(1)}`, `${sorted[category][command].meta.description}\n\nAliases: ${aliases}\nSyntax: ${sorted[category][command].meta.syntax}${sorted[category][command].meta.extra ? '\n\n' + sorted[category][command].meta.extra : ''}`);
current += 1;
if (current === 5) {
categorySorted.push(currentEmbed);
current = 1;
currentEmbed = new Discord.MessageEmbed().setAuthor({name: "Help Menu", iconURL: message.author.displayAvatarURL()}).setTitle(category).setDescription("React to control the menu! You can also specify a command name when doing the help command to get more info about it.").setColor("c375f0");
}
}
if (current > 1) {categorySorted.push(currentEmbed);}
helpSorted[category] = categorySorted;
}
let cle = await message.channel.send({embeds: [
new Discord.MessageEmbed()
.setTitle("Natsuki Help")
.setDescription(`Here you can find a list of commands and how to use them.\n\nNatsuki's prefix, by default, is \`n?\`. Here, it's \`${prefix}\`.\n\nWhen viewing a command's syntax, a parameter/argument marked with <> means that it is required. [] shows that it is optional.\n\nGet more help on a command by sending it without any arguments (i.e. \`${prefix}anime\`), or run \`${prefix}help <command>\`.`)
.addField("Category", "What category would you like to view?\n:one: - Fun\n:two: - Utility\n:three: - Misc\n:four: - Developer\n:five: - Moderation\n:six: - Social\n:seven: - Leveling\n:eight: - Anime\n:nine: - **All**")
.setColor('c375f0')
.setFooter({text: "Natsuki | Will time out in 60 seconds."})
.setThumbnail(client.user.displayAvatarURL({size: 2048}))
.setTimestamp()
]});
let pages;
let nums = {
'1': "Fun",
'2': "Utility",
'3': "Misc",
'4': "Developer",
'5': "Moderation",
'6': "Social",
'7': "Leveling",
'8': "Anime",
'9': "All"
};
Object.keys(nums).forEach(num => cle.react(num).catch(() => {}));
let donePre = false;
const getCat = () => {
return new Promise(r => {
message.channel.awaitMessages({filter: m => m.author.id === message.author.id, max: 1, errors: ['time'], time: 60000}).then(cat => {
if (donePre) {return;}
cat = cat.first().content;
if (['f', 'fun', 'u', 'util', 'utility', 'utilities', 'm', 'misc', 'miscellaneous', 'mod', 'moderation', 's', 'social', 'leveling', 'l', 'level', 'ani', 'anime', 'a', 'all'].includes(`${cat}`.trim().toLowerCase())) {
if (['f', 'fun'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Fun'];}
if (['u', 'util', 'utility', 'utilities'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Utility'];}
if (['m', 'misc', 'miscellaneous'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Misc'];}
if (['d', 'dev', 'developer'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Developer'];}
if (['mod', 'moderation'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Moderation'];}
if (['s', 'social'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Social'];}
if (['l', 'leveling', 'level'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Leveling'];}
if (['ani', 'anime'].includes(`${cat}`.trim().toLowerCase())) {pages = helpSorted['Anime'];}
if (['a', 'all'].includes(`${cat}`.trim().toLowerCase())) {pages = []; let c; for (c of Object.values(helpSorted)) {let h; for (h of c) {pages.push(h)}}}
donePre = true;
r(true);
} else {donePre = true; r(false);}
}).catch(() => {donePre = true; r(false);});
cle.awaitReactions({filter: (rt, u) => Object.keys(nums).includes(rt.emoji.name) && u.id === message.author.id, time: 60000, errors: ['time'], max: 1}).then(collected => {
if (donePre) {return;}
if (nums[collected.first().emoji.name] === 'All') {pages = []; let c; for (c of Object.values(helpSorted)) {let h; for (h of c) {pages.push(h)}}}
else {pages = helpSorted[nums[collected.first().emoji.name]];}
donePre = true;
r(true);
}).then(() => {donePre = true; r(false);});
});
}
if (!await getCat().catch(() => {})) {return;}
await require('../../util/wait')(500);
cle.delete().catch(() => {});
if (pages.length > 1) {
let help = new Pagination(message.channel, pages, message, client, true);
return await help.start({endTime: 60000, user: message.author.id}).catch(() => {});
} else {return message.channel.send({embeds: [pages[0].setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()}).setTimestamp()]}).catch(() => {});}
} else {
let command;
if (client.commands.has(args[0])) {command = client.commands.get(args[0]);}
else if (client.aliases.has(args[0])) {command = client.commands.get(client.aliases.get(args[0]));}
else {return message.reply("I don't have that command! Try using `" + prefix + "help` to get a list of my commands");}
return message.reply(command.help
? command.help instanceof Discord.MessageEmbed
? {embeds: [command.help.setFooter({text: "Natsuki | <required> [optional]", iconURL: client.user.displayAvatarURL()}).setColor("c375f0").setTimestamp()]}
: command.help.replace(/{{p}}/g, prefix)
: "I don't seem to have any help info available for that command."
);
}
}
};

@ -1,35 +0,0 @@
const Discord = require("discord.js");
const moment = require('moment');
const os = require('os');
const UserData = require('../../models/user');
module.exports = {
name: "info",
aliases: ["i", "botinfo", "bot"],
help: "There's not really anything to help with here! Just use `{{p}}info` to learn more about me!",
meta: {
category: 'Misc',
description: "Get info about me, my creators, and my status.",
syntax: '`info`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let botData = await require('../../models/bot').findOne({finder: 'lel'});
let user = await UserData.findOne({uid: message.author.id});
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: "About Me!", iconURL: client.users.cache.get(client.developers[Math.floor(Math.random() * client.developers.length)]).displayAvatarURL()})
.setThumbnail(client.user.displayAvatarURL({size: 1024}))
.setDescription(`I am created by WubzyGD#8766 and Slushie#1234 - a pair conveniently known as NatsukiDev - in JavaScript/Discord.js!\n\nI'm a powerful all-purpose bot with everything you could want or need, and I have my own set of unique skills that you won't find anywhere else ^^`)
.addField("Presence", `I'm currently in **${client.guilds.cache.size}** servers, and I'm watching over approximately **${client.users.cache.size}** people!`)
.addField("Restarts", `${botData.restarts}`, true)
.addField("Commands Executed", `${botData.commands}${user ? `\nYou: **${user.commands}** | **${Math.floor((user.commands / botData.commands) * 100)}%**` : ''}`, true)
.addField("Last Restart", moment(botData.lastRestart).fromNow(), true)
.addField("Mem", `\`${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB\` heap of \`${(process.memoryUsage().heapTotal / 1024 / 1024).toFixed(2)}MB\` allocated. | **${Math.floor((process.memoryUsage().heapUsed / process.memoryUsage().heapTotal) * 100)}%**\nTotal RAM: \`${(os.totalmem() / 1024 / 1024 / 1024).toFixed(2)}GB\` | Free RAM: \`${(os.freemem() / 1024 / 1024 / 1024).toFixed(2)}GB\``, true)
.setColor("c375f0")
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
}
};

@ -1,48 +0,0 @@
const Discord = require('discord.js');
const AR = require('../../models/ar');
const GuildData = require('../../models/guild');
module.exports = {
name: "ignorear",
aliases: ['arignore', 'noar'],
meta: {
category: 'Misc',
description: "Stop auto responses from being sent to a specific channel.",
syntax: '`ignorear [#channel|channelId]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> AR Ignoring")
.setDescription("Provide a channel (or don't to use the current channel) to be voided from auto-responses, that way the responses won't send in places you don't want them to.")
.addField("Syntax", "`[#channel|channelId]` - channel is optional."),
async execute(message, msg, args, cmd, prefix, mention, client) {
const tg = await GuildData.findOne({gid: message.guild.id});
if ((!tg || !tg.staffrole || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You must have the staff role or be an administrator in this server in order to edit AR settings.");}
let tar = await AR.findOne({gid: message.guild.id});
if (!tar || !tar.triggers.length) {return message.channel.send("This server doesn't have any auto-responses. Try adding some first, then you can set some channels to be ignored.");}
let ch;
if (args[1]) {
ch = message.mentions.channels.first() || message.guild.channels.cache.get(args[1]);
if (!ch) {return message.channel.send("I couldn't find that channel. Please try again!");}
} else {ch = message.channel;}
ch = ch.id;
if (tar.ignoreChs.includes(ch)) {
let ti = tar.ignoreChs;
ti.splice(ti.indexOf(ch), 1);
tar.ignoreChs = ti;
tar.markModified('ignoreChs');
tar.save();
client.misc.cache.arIgnore.set(message.guild.id, tar.ignoreChs);
return message.channel.send("I'll start replying to Auto Responses in this channel from now on.");
} else {
tar.ignoreChs.push(ch);
tar.save();
client.misc.cache.arIgnore.set(message.guild.id, tar.ignoreChs);
return message.channel.send("Got it. I'll ignore Auto Responses here from now on.");
}
}
};

@ -1,23 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "invite",
aliases: ['inv', 'botinvite'],
meta: {
category: 'Misc',
description: "Get the bot invite and support server invite",
syntax: '`invite`',
extra: null
},
help: "Shows you my invite and support server invite",
async execute(message, msg, args, cmd, prefix, mention, client) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("My links!")
.setThumbnail(client.user.displayAvatarURL({size: 2048}))
.setDescription("[Bot Invite](https://discord.com/api/oauth2/authorize?client_id=762701327431237644&permissions=1507482660086&scope=bot%20applications.commands)\n`->` Use this link to invite Natsuki to your server! This has all the required permissions in it that the I need to work, and it is not recommended that you change them. Doing so will make it so that some commands don't function properly or won't complete (I'll usually tell you when I'm missing a permission).\n\n[Support Server Invite](https://discord.gg/u9c2uD24wB)\n`->` Use this to join my support server! Here you can talk to the devs, suggest features, hang out with the community, get update alerts, report bugs/issues and get help, or just stop and say hi!")
.setColor("c375f0")
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
}
};

@ -1,40 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const ask = require("../../util/ask");
module.exports = {
name: "lfm",
aliases: ['lastfm'],
meta: {
category: 'Misc',
description: "Interact with the last.fm service",
syntax: '` <>`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Last.fm")
.setDescription("Interact with last.fm, a service that stores and analyzes your Spotify listening activity.")
.addField("Notice", "Most of the features of this command require you to have an account. You can make one at [last.fm](https://last.fm)")
.addField("Syntax", "``"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}\``);}
let savess = await Saves.findOne({name: 'lfm'}) || new Saves({name: 'lfm'});
let saves = savess.saves;
if (['s', 'set', 'setname', 'setusername', 'setn'].includes(args[0].toLowerCase())) {
args.shift();
let name;
if (!args.length) {
name = await ask(message, "What is your last.fm username?", 60000);
if (!name) {return;}
}
saves.set(message.author.id, name || args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send(`Last.fm username set! Try \`${prefix}nowplaying\` if you're listening to music to show off what you're currently listening to!`);
}
}
};

@ -1,21 +0,0 @@
const Discord = require('discord.js');
const os = require('os');
module.exports = {
name: "mem",
aliases: ['memory', 'ram', 'memstats'],
meta: {
category: 'Misc',
description: "Shows memory usage stats",
syntax: '`mem`',
extra: null
},
help: "shows my memory usage stats",
async execute(message, msg, args, cmd, prefix, mention, client) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("RAM Usage")
.setDescription(`\`${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB\` heap of \`${(process.memoryUsage().heapTotal / 1024 / 1024).toFixed(2)}MB\` allocated. | **${Math.floor((process.memoryUsage().heapUsed / process.memoryUsage().heapTotal) * 100)}%**\nTotal RAM: \`${(os.totalmem() / 1024 / 1024 / 1024).toFixed(2)}GB\` | Free RAM: \`${(os.freemem() / 1024 / 1024 / 1024).toFixed(2)}GB\``)
.setColor('c375f0')
]});
}
};

@ -1,22 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "ping",
aliases: [],
meta: {
category: 'Misc',
description: "Find out Natsuki's latency",
syntax: '`ping`',
extra: null
},
help: "Definitely not much to see here...",
async execute(message, msg, args, cmd, prefix, mention, client) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Client Latency")
.setDescription(`Response Latency: \`${new Date().getTime() - message.createdTimestamp}ms\`\nAPI Latency: \`${client.ws.ping}ms\``)
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
.setColor('c375f0')
]});
}
};

@ -1,48 +0,0 @@
const Discord = require('discord.js');
const mongoose = require('mongoose');
const GuildSettings = require('../../models/guild');
module.exports = {
name: "prefix",
help: new Discord.MessageEmbed()
.setTitle("Help -> Prefix")
.setDescription("Changes your server's prefix.")
.addField("Syntax", "`prefix <newPrefix|clear>`")
.addField("Staff Command", "This command requires you to either be admin, or to have the designated staff role.")
.addField("Notice", "Prefixes are cached, and may take up to a minute to update."),
meta: {
category: 'Misc',
description: "Change my prefix in your server.",
syntax: '`prefix <newPrefix|clear>`',
extra: "You can always mention me and then type your command in place of a prefix in case you forget it."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This is a guild-only command!");}
let tguild = await GuildSettings.findOne({gid: message.guild.id})
? await GuildSettings.findOne({gid: message.guild.id})
: new GuildSettings({gid: message.guild.id});
if (!message.member.permissions.has("ADMINISTRATOR") && (!tguild.staffrole.length || !message.guild.roles.cache.has(tguild.staffrole) || !message.member.roles.cache.has(tguild.staffrole))) {return message.reply("You don't have the permissions to use this command here.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix} <newPrefix|clear>\`. My current prefix in this server is \`${tguild.prefix.length ? tguild.prefix : 'n?'}\``);}
let np = args[0];
if (np.length > 7) {return message.reply("Hmmm, that prefix is a bit long. Try making something smaller!");}
if (!np.match(/^[a-zA-Z0-9,.!?<>\-_+=/;$#%^&*]+$/)) {return message.reply('Your custom prefix contains some *wonky* characters. Please use only alphanumerics and basic symbols.');}
tguild.prefix = ['c', 'clear', 'n', 'none'].includes(np.trim().toLowerCase()) ? '' : np;
tguild.save();
if (['c', 'clear', 'n', 'none'].includes(np.trim().toLowerCase())) {
client.guildconfig.prefixes.set(message.guild.id, null);
return message.reply('this server\'s prefix has been reset to the default, `n?`.');
}
client.guildconfig.prefixes.set(message.guild.id, np);
let upm = await message.reply("sure thing!");
await require('../../util/wait')(1750);
return upm.edit({embeds: [new Discord.MessageEmbed()
.setAuthor({name: 'Prefix updated!', iconURL: message.author.displayAvatarURL()})
.setDescription(`New prefix: \`${np}\``)
.addField('Auditing Admin', `<@${message.member.id}>`, true)
.addField("Notice", "Prefixes are cached, and may take up to a minute to update.")
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
};

@ -1,42 +0,0 @@
const Discord = require("discord.js");
const moment = require('moment');
require('moment-precise-range-plugin');
module.exports = {
name: "serverinfo",
aliases: ['si'],
help: "Displays your server's information",
meta: {
category: 'Misc',
description: "Displays your server's information",
syntax: '`serverinfo`',
extra: null,
guildOnly: true
},
execute(message, msg, args, cmd, prefix, mention, client) {
let now = new Date();
let tg = message.guild;
let text = ["GUILD_MESSAGE", "DM", "GUILD_NEWS_THREAD", "GUILD_PRIVATE_THREAD", "GUILD_PUBLIC_THREAD", "GUILD_NEWS", "GROUP_DM", "GUILD_STORE", "GUILD_TEXT"];
let voice = ["GUILD_VOICE", "GUILD_STAGE_VOICE"];
let siembed = new Discord.MessageEmbed()
.setAuthor({name: "Server info", iconURL: message.author.displayAvatarURL({dynamic: true})})
.setTitle(tg.name)
.setThumbnail(tg.iconURL({size: 2048, dynamic: true, format: 'png'}))
.setDescription(`Name: \`${tg.name}\`\n\nOwner: <@${tg.ownerId}>\nBoost Level: **${tg.premiumTier === 'NONE' ? 'None' : tg.premiumTier.slice(tg.premiumTier.length - 1, tg.premiumTier.length)}**\nIcon: [URL](${tg.iconURL({size: 2048})})${tg.banner ? ` | Banner: [URL](${tg.bannerURL({size: 4096, format: 'png'})})` : ''}${tg.splash ? ` | Splash: [URL](${tg.splashURL({size: 4096, format: 'png'})})` : ''}\nID: ${tg.id}`)
.addField("Channels", `${tg.channels.cache.filter(ch => ch.type !== 'GUILD_CATEGORY').size}\n[${tg.channels.cache.filter(ch => text.includes(ch.type)).size} Text | ${tg.channels.cache.filter(ch => voice.includes(ch.type)).size} Voice]`, true)
.addField("Roles", `${tg.roles.cache.size} (you have ${message.member.roles.cache.size}) -> ${Math.round((message.member.roles.cache.size / tg.roles.cache.size) * 100)}%\nYour highest is <@&${message.member.roles.highest.id}>`, true)
.addField("Members", `${tg.memberCount}\n[${tg.members.cache.filter(m => !client.users.cache.get(m.id).bot).size} Humans | ${tg.members.cache.filter(m => client.users.cache.get(m.id).bot).size} Bots]\n\nOnline: ${tg.members.cache.filter(m => m.presence && m.presence.status === "online").size} | Idle: ${tg.members.cache.filter(m => m.presence && m.presence.status === "idle").size} | Do not Disturb: ${tg.members.cache.filter(m => m.presence && m.presence.status === "dnd").size}`)
.addField("Emojis", `${tg.emojis.cache.size}`, true)
.addField("Stickers", `${tg.stickers.cache.size}`, true)
.addField("Other Info", `Server created **${client.utils.sm(moment.preciseDiff(Date.now(), tg.createdAt, true))}**\nYou joined ${client.utils.sm(moment.preciseDiff(Date.now(), message.member.joinedAt, true))} (Member for **${Math.round(((now.getTime() - message.member.joinedAt.getTime()) / (now.getTime() - message.guild.createdAt.getTime())) * 100)}%** of server lifetime)`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp();
if (tg.banner) {siembed.setImage(tg.bannerURL({size: 4096, format: 'png'}));}
else if (tg.splash) {siembed.setImage(tg.splashURL({size: 4096, format: 'png'}));}
if (tg.premiumTier !== 'NONE') {siembed.addField("Features", tg.features.map(f => client.utils.ca(f.replace(/_/gm, ' '))).join(", "));}
return message.channel.send({embeds: [siembed]});
}
};

@ -1,54 +0,0 @@
const Discord = require('discord.js');
const UserData = require("../../models/user");
module.exports = {
name: "sleeping",
aliases: ['asleep', 'sloopin', 'ischleep'],
meta: {
category: 'Misc',
description: "Set your status to tell others you're sleeping.",
syntax: '`sleeping`',
extra: null
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Sleeping")
.setDescription("Use this command to set an automatically-clearing AFK status that lets people who ping you know you're asleep. Send any message to any server with Natsuki in it to clear the status.")
.addField("Syntax", "`sleeping`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
let tu = await UserData.findOne({uid: message.author.id}) || new UserData({uid: message.author.id});
tu.statusclearmode = 'auto';
tu.statustype = 'afk';
tu.statusmsg = [
"<a:NC_yawn:830701611990908961> Sleeping",
"<a:NC_yawn2:857589454830174219> Getting a good night's rest",
"<a:NC_yawn:830701611990908961> Big sleep",
"<a:NC_yawn2:857589454830174219> \\*snore\\*"
][Math.floor(Math.random() * 4)];
tu.statussetat = new Date();
let tempDate = new Date();
tu.statusclearat = tempDate.setHours(tempDate.getHours() + 12);
tu.statusSleeping = true;
tu.markModified("statusSleeping");
tu.markModified("statussetat");
tu.markModified("statusclearat");
tu.save();
require('../../util/cachestatus')(message.author.id, tempDate.setHours(tempDate.getHours() + 10));
return message.channel.send(
(client.misc.cache.returnToSleep.has(message.author.id) && new Date().getTime() - client.misc.cache.returnToSleep.get(message.author.id) < 600000
? [
"Ah, well I'm glad you're getting back to sleep! Try to put the phone down and get some rest qt",
"Actually taking my advice and getting some sleep? You need it after all!",
":D You're going back to bed! I don't wanna see you up again. You need some sleep qt ^^",
"Sleep is good for you! It's best uninterrupted. Get some good sleep and try not to pick up your phone again."
]
: [
"Goodnight qt <:NC_wave:830704926576345119>",
"<:NC_hearty:841489530413383712> Have a good sleep! ^^",
"Sleep well. You deserve it <3 <a:NC_NekoPet_1:861664617406136342><a:NC_NekoPet_2:861664756184252417>",
"<:NC_nezuGUN:852735951712157698> Get good sleep coward!"
]
)[Math.floor(Math.random() * 4)]
);
}
};

@ -1,23 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "supportserver",
aliases: ['helpserver', 'support'],
meta: {
category: 'Misc',
description: "Get an invite to Natsuki's support server!",
syntax: '`supportserver`',
extra: null
},
help: "Get an invite to Natsuki's support server!",
async execute(message, msg, args, cmd, prefix, mention, client) {
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Sure thing!")
.setThumbnail(client.user.displayAvatarURL({size: 2048}))
.setDescription("Join the server with [this link](https://discord.gg/u9c2uD24wB)!\n\n`->` Here you can talk to the devs, suggest features, hang out with the community, get update alerts, report bugs/issues and get help, or just stop and say hi!")
.setColor("c375f0")
.setFooter({text: "Natsuki"})
.setTimestamp()
]});
}
};

@ -1,28 +0,0 @@
const Discord = require('discord.js');
const Bot = require('../../models/bot');
const moment = require('moment');
require('moment-precise-range-plugin');
module.exports = {
name: "uptime",
aliases: ['ut', 'up'],
meta: {
category: 'Misc',
description: "Shows the bot's uptime",
syntax: '`uptime`',
extra: null
},
help: "Shows my uptime, which is how long it's been since my last restart.",
async execute(message, msg, args, cmd, prefix, mention, client) {
const bot = await Bot.findOne({finder: 'lel'});
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Uptime")
.setDescription(`${moment.preciseDiff(moment(bot.lastRestart), moment())}`)
.setColor('c375f0')
.setFooter({text: "Natsuki"})
.setTimestamp()
]})
}
};

@ -1,55 +0,0 @@
const Discord = require('discord.js');
const moment = require('moment');
require('moment-precise-range-plugin');
const UserData = require('../../models/user');
module.exports = {
name: "userinfo",
aliases: ['ui', 'memberinfo', 'user'],
help: "Shows your info, or shows the info of a user you ping.",
meta: {
category: 'Misc',
description: "See some info about a user",
syntax: '`userinfo [@user]`',
extra: null,
guildOnly: true
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let person = message.mentions.members.first() || message.guild.members.cache.get(args[0]) || message.member;
if (!person) {return message.channel.send("Hmmm... that doesn't seem to be a real user.");}
let u = await client.users.fetch(person.id, {force: true, cache: false}).catch(() => {});
if (!u) {return message.channel.send("There was an issue finding that user. They might not be a real user!");}
temp = await message.guild.members.fetch(person.id);
if (!temp) {return message.channel.send("There was an issue finding that user. They might not be a real user, or not one that exists in this server.");}
let name = message.guild ? person.displayName : person.username;
let tu = await UserData.findOne({uid: person.id});
let now = new Date();
let infoembed = new Discord.MessageEmbed()
.setTitle(`User Info for ${name}`)
.setDescription(`Requested by ${message.guild ? message.member.displayName : message.author.username}`)
.setThumbnail((message.guild ? person : u).displayAvatarURL({size: 4096, dynamic: true, format: 'png'}))
.addField("Account Created", `${moment.preciseDiff(Date.now(), u.createdAt)} ago`, true)
.addField("Bot User?", u.bot ? "Is a bot" : "Is not a bot", true)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
if (message.guild) {
infoembed.addField('In Server Since', `${client.utils.sm(moment.preciseDiff(Date.now(), person.joinedAt, true))}${!moment(person.joinedAt).fromNow().includes('days') ? ` | ${Math.floor((new Date().getTime() - person.joinedAt.getTime()) / (60 * 60 * 24 * 1000))} days` : ''}\nMember for **${Math.round(((now.getTime() - person.joinedAt.getTime()) / (now.getTime() - message.guild.createdAt.getTime())) * 100)}%** of server lifetime`, false)
.addField('Roles', `**${person.roles.cache.size}** roles | [${person.roles.cache.size}/${message.guild.roles.cache.size}] - ${Math.round((person.roles.cache.size / message.guild.roles.cache.size) * 100)}%\nHighest: ${person.roles.highest ? `<@&${person.roles.highest.id}>` : 'No roles!'}`, true)
if (message.guild.ownerId === person.id) {infoembed.addField("Extra", "User is the server's owner!");}
else if (person.permissions.has("ADMINISTRATOR")) {infoembed.addField("Extra", "User is an admin! Watch out :eyes:");}
}
if (tu) {
infoembed.addField('Natsuki Commands Executed', `${tu.commands}`);
if (tu.donator || tu.developer) {infoembed.addField('Donator?', tu.developer ? `Well, ${name} makes me work, so they're a supporter in my book!` : tu.donator ? 'Yes! They have donated or supported me in the past!' : 'No', true);}
if (tu.staff) {infoembed.addField('Natsuki Staff Level', tu.developer ? 'Developer' : tu.admin ? 'Admin; Audit access to the bot' : tu.staff ? 'Staff; Support but with maintenance permissions' : tu.support ? 'Support; Answers tickets and help queries' : 'Member; Does not have a staff rank.', true);}
}
if (u.banner) {infoembed.setImage(u.bannerURL({size: 4096, dynamic: true, format: 'png'}));}
return message.channel.send({embeds: [infoembed]});
}
};

@ -1,39 +0,0 @@
const Discord = require('discord.js');
const GuildData = require('../../models/guild');
module.exports = {
name: "autorole",
aliases: ['joinrole', 'jr'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Auto Role/Join Role")
.setDescription("Set a role to be automatically added to users when they join the server.")
.addField("Syntax", "`autorole <set|clear|view>`")
.addField('Notice', "This command can only be used by server staff members and admins."),
meta: {
category: 'Moderation',
description: "Set a role to be automatically added when a member joins the server.",
syntax: '`autorole <set|clear|view>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This command is only available in servers.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}autorole <set|clear|view>\``);}
let tg = await GuildData.findOne({gid: message.guild.id}) ? await GuildData.findOne({gid: message.guild.id}) : new GuildData({gid: message.guild.id});
if (['v', 'view', 'check'].includes(args[0])) {return message.channel.send(tg.joinrole.length && message.guild.roles.cache.has(tg.joinrole) ? `I am currently adding \`${message.guild.roles.cache.get(tg.joinrole).name}\` to new members.` : "At the moment, I'm not adding a role to new members.");}
if ((!tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You don't have the permissions to edit this setting.");}
if (['s', 'set', 'c', 'clear'].includes(args[0])) {
let role = message.mentions.roles.first() ? message.mentions.roles.first().id : args[1] && message.guild.roles.cache.has(args[1]) ? args[1] : ['c', 'clear'].includes(args[0]) ? '' : null;
if (role === null) {return message.reply("That doesn't seem to be a role!");}
tg.joinrole = role;
tg.save();
return message.channel.send(new Discord.MessageEmbed()
.setTitle("Join Role Updated")
.setThumbnail(message.author.displayAvatarURL({size: 2048}))
.setDescription(`Role: ${tg.joinrole.length ? `<@&${tg.joinrole}>` : "None"}`)
.setColor("c375f0")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
);
}
}
};

@ -1,73 +0,0 @@
const Discord = require('discord.js');
const Mod = require('../../models/mod');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
module.exports = {
name: "ban",
aliases: [],
meta: {
category: 'Moderation',
description: "Bans a member from the server!",
syntax: '`ban <@member|memberID> [reason]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Member Banning")
.setDescription("This command bans a member from the server permanently, making it so they cannot rejoin. *Yikes*")
.addField("Syntax", "`ban <@member|memberID> [reason]`")
.addField("Permissions", "You'll want to have the `ban members` permission in your server or be an administrator to do this!"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}ban <@member|memberID> [reason]\``);}
if (!message.member.permissions.has("BAN_MEMBERS")) {return message.channel.send("You don't have permissions to do that!");}
if (!message.guild.me.permissions.has("BAN_MEMBERS")) {return message.channel.send("I don't have permissions to ban members in your server.");}
let user = message.guild.members.cache.get(args[0]) || message.mentions.members.first();
if (!user) {return message.channel.send("You must mention a user to ban, or provide their ID.");}
if (user.roles.highest.position >= message.member.roles.highest.position) {return message.channel.send("You don't have permissions to ban that member as they are above you in the roles list.");}
if (user.roles.highest.position >= message.guild.me.roles.highest.position) {return message.channel.send("I can't ban that member as their highest role is above mine! (Or the same as mine, too)");}
if (!user.bannable) {return message.channel.send("Hmm, it seems like I can't ban that member. This is probably a permissions issue. Or maybe they were already banned?");}
let options = new TagFilter([
new Tag(['r', 'reason'], 'reason', 'append'),
new Tag(['n', 'notes'], 'notes', 'append'),
new Tag(['d', 'days', 'm', 'messages'], 'days', 'append')
]).test(args.join(" "));
let reason; let days;
if (options.reason && options.reason.length) {reason = options.reason;}
if (options.days && options.days.length) {
if (isNaN(Number(options.days)) || Number(options.days) < 0 || Number(options.days) > 7 || options.days.includes('.')) {return message.channel.send("The `days` option must be a whole number between 0 and 7.");}
days = Number(options.days);
}
if (options.notes && options.notes.length > 250) {return message.channel.send("I mean I get it, they pissed you off, but do you really need to give me that much info on why you're banning them? I can't keep track of all that!");}
else {if (args[1] && !options.days /*&& (!options.notes || !options.notes.length)*/ && (!options.reason || !options.reason.length)) {args.shift(); reason = args.join(" ");}}
if (reason && reason.length > 250) {return message.channel.send("I mean I get it, they pissed you off, but do you really need to give me that much info on why you're banning them? I can't keep track of all that!");}
return message.guild.members.ban(user.id, {reason: reason, days: typeof days === "number" ? days : 0})
.then(async () => {
/*let mh = await Mod.findOne({gid: message.guild.id}) || new Mod({gid: message.guild.id});
let mhcases = mh.cases;
mhcases.push({
members: [user.id],
punishment: "Banned",
reason: reason ? reason : "",
status: "Closed",
moderators: [message.author.id],
notes: options.notes,
history: [`${new Date().toISOString()} - ${message.author.username} - Created case`, `${new Date().toISOString()} - ${message.author.username} - Banned ${client.users.cache.get(user.id).username}`],
issued: new Date().toUTCString()
});
mh.cases = mhcases;
mh.save();*/
return message.channel.send(`The hammer of justice has spoken!${reason ? ` Reason for banning: ${reason}` : ''}`);
})
.catch(() => {return message.channel.send("Something went wrong while trying to ban that user! If the problem persists, contact my devs.");});
}
};

@ -1,50 +0,0 @@
const Discord = require('discord.js');
const Mod = require('../../models/mod');
module.exports = {
name: "checkwarnings",
aliases: ['checkwarn', 'chw', 'warncheck', 'checkwarning'],
meta: {
category: 'Moderation',
description: "Check a user's warnings in your server.",
syntax: '`checkwarnings [@user|userID]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Warn Clearing")
.setDescription("Checks the warnings of a user")
.addField("Syntax", "`checkwarnings [@user|userID]`")
.addField("Notice", "You must be a server moderator in order to use this command."),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}checkwarnings [@user|userID]\``);}
if (!message.member.permissions.has("MANAGE_MESSAGES") && !message.member.permissions.has("MANAGE_GUILD")) {return message.reply("You must be a server moderator (manage messages or manage server permissions) to use this command.");}
let user = message.mentions.members.first() && args[0].match(/^<@(?:!)(?:\d+)>$/) ? message.mentions.members.first() : message.guild.members.cache.has(args[0]) ? message.guild.members.cache.get(args[0]) : message.member;
let mh = await Mod.findOne({gid: message.guild.id});
if (!mh || !Object.keys(mh.warnings).length) {return message.reply("There are no warnings available in this server.");}
if (!mh.warnings[user.id] || !mh.warnings[user.id].length) {return message.reply(`${user.id === message.author.id ? 'You have' : 'That user has'} never been warned in this server.`);}
//console.log(mh.cases, mh.warnings);
let ws = '';
let cwc = 0;
let warning; for (warning of mh.warnings[user.id]) {
let tcase = mh.cases[warning - 1];
if (tcase.status !== "Cleared") {
ws += `\`Case #${warning}\` - Issued by <@${tcase.moderators[0]}>\n${tcase.reason}\n\n`;
} else {cwc++;}
}
if (cwc > 0) {ws += '*Plus ' + cwc + ' other warnings that have been cleared.*';}
if (cwc === mh.warnings[user.id].length) {return message.reply("That user has no uncleared warnings.");}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("User Warnings")
.setThumbnail(client.users.cache.get(user.id).displayAvatarURL({size: 1024}))
.setDescription(`For ${user.displayName}`)
.addField("Warnings", ws)
.setColor("c375f0")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
};

@ -1,58 +0,0 @@
const Discord = require('discord.js');
const Mod = require('../../models/mod');
module.exports = {
name: "clearwarnings",
aliases: ['clearwarn', 'cw', 'warnclear', 'wc', 'clearwarning'],
meta: {
category: 'Moderation',
description: "Clear a user's warnings in your server.",
syntax: '`clearwarnings <@user|userID>`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Warn Clearing")
.setDescription("Clears the warnings of a user")
.addField("Syntax", "`clearwarnings <@user|userID>`")
.addField("Notice", "You must be a server moderator in order to use this command."),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}clearwarnings <@user|userID>\``);}
if (!message.member.permissions.has("MANAGE_MESSAGES") && !message.member.permissions.has("MANAGE_GUILD")) {return message.reply("You must be a server moderator (manage messages or manage server permissions) to use this command.");}
let user = message.mentions.members.first() && args[0].match(/^<@(?:!)(?:\d+)>$/) ? message.mentions.members.first() : message.guild.members.cache.has(args[0]) ? message.guild.members.cache.get(args[0]) : null;
if (!user) {return message.channel.send("Either you didn't mention a user, or I can't find that user!");}
if (user.id === client.user.id) {return message.reply("don't worry about clearing any warnings from me... you can't give me warnings in the first place");}
if (client.users.cache.get(user.id).bot) {return message.reply("it's not like a bot would have any warnings in the first place...");}
user = user ? user : message.member;
let mh = await Mod.findOne({gid: message.guild.id});
if (!mh || !Object.keys(mh.warnings).length) {return message.reply("There are no warnings available in this server.");}
if (!Object.keys(mh.warnings).includes(user.id) || !mh.warnings[user.id].length) {return message.reply(`${user.id === message.author.id ? 'You have' : 'That user has'} never been warned in this server.`);}
let mhcases = mh.cases;
let moddedcases = [];
let cwc = 0; var wc = 0;
let warning; for (warning of mh.warnings[user.id]) {
if (mhcases[`${warning - 1}`].status !== "Cleared") {
let tcase = mhcases[`${warning - 1}`];
tcase.status = "Cleared";
tcase.history.push(`${new Date().toISOString()} - ${message.author.username} - Cleared the warning.`);
moddedcases.push(`${warning - 1}`);
wc++;
if (!tcase.moderators.includes(message.author.id)) {tcase.moderators.push(message.author.id);}
mhcases[`${warning - 1}`] = tcase;
} else {cwc++;}
}
if (cwc === mh.warnings[user.id].length) {return message.reply("That user has no uncleared warnings.");}
if (moddedcases.length) {let c; for (c of moddedcases) {mh.markModified(`cases.${c}.history`);}}
mh.cases = mhcases;
mh.save();
return message.reply(`Cleared ${wc} warnings from ${user.displayName}.`);
}
};

@ -1,38 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "delete",
aliases: ['del', 'purge', 'clear'],
meta: {
category: 'Moderation',
description: "Delete messages from a channel, and optionally from a specific user or users.",
syntax: '`clear <messageCount> [@user] [@user] [etc]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Mass Message Deletion")
.setDescription("Delete recently-sent messages from a channel. You can also delete them from a specific user or users, if you wish.")
.addField("Permissions", "You must have Manage Messages permissions in this server in order to purge messages.")
.addField("Mentions", "If you mention multiple people, I will delete messages from those users until the total sum of messages reaches the specified message count. In other words, if you mention three people and specify to delete 6 messages, I will the last 6 messages from all of them, not the last 6 messages for each user.")
.addField("Notice", "Purges with mentions might purge less than the specified count. This only really affects large deletion counts in channels where several other people than just the members mentioned talked. A fix is currently being made.")
.addField("Syntax", "`clear <messageCount> [@user] [@user] [etc]`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.member.permissions.has("MANAGE_MESSAGES")) {return message.channel.send("You don't have permissions to manage messages in this server!");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}clear <messageCount> [@user] [@user] [etc]\``);}
if (!message.guild.me.permissions.has("MANAGE_MESSAGES")) {return message.channel.send("I don't have permissions to manage messages in this server.");}
if (!message.channel.permissionsFor(client.user.id).has("MANAGE_MESSAGES")) {return message.channel.send("I don't have permissions to manage messages in this channel.");}
if (isNaN(Number(args[0])) || Number(args[0]) > 100 || Number(args[0]) < 1) {return message.channel.send("You must specify a positive number less than 100 of messages to delete.");}
await message.delete();
let count = Number(args[0]);
while (true) {
let messages = await message.channel.messages.fetch({limit: message.mentions.users.size ? 50 : count}, false, true).catch(() => message.channel.send("There was an error trying to grab the messages to delete. Sorry!"));
let toDelete = [];
if (message.mentions.users.size) {Array.from(message.mentions.users.values()).forEach(u => Array.from(messages.values()).forEach(m => {if (m.author.id === u.id && toDelete.length < count) {toDelete.push(m.id);}}));}
else {toDelete = Array.from(messages.keys());}
await message.channel.bulkDelete(toDelete, true);
if (toDelete.length >= count) {break;}
}
return true;
}
};

@ -1,68 +0,0 @@
const Discord = require('discord.js');
const Mod = require('../../models/mod');
const {Tag} = require('../../util/tag');
const {TagFilter} = require('../../util/tagfilter');
module.exports = {
name: "kick",
aliases: ['kicc', 'k'],
meta: {
category: 'Moderation',
description: "Kicks a user from the server!",
syntax: '`kick <@user|userID> [reason]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Kick")
.setDescription("Kicks a user from the server!")
.addField("Syntax", "`kick <@user|userID> [reason]`")
.addField("Notice", "This command requires you to have `kick` permissions in the server."),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}kick <@user|userID> [reason]\``);}
if (!message.member.permissions.has("KICK_MEMBERS")) {return message.channel.send("You don't have permissions to do that!");}
if (!message.guild.me.permissions.has("KICK_MEMBERS")) {return message.channel.send("I don't have permissions to kick members in your server.");}
let user = message.guild.members.cache.get(args[0]) || message.mentions.members.first();
if (!user) {return message.channel.send("You must mention a user to kick, or provide their ID.");}
if (user.roles.highest.position >= message.member.roles.highest.position) {return message.channel.send("You don't have permissions to kick that member as they are above you in the roles list.");}
if (user.roles.highest.position >= message.guild.me.roles.highest.position) {return message.channel.send("I can't kick that member as their highest role is above mine! (Or the same as mine, too)");}
if (!user.kickable) {return message.channel.send("For some reason, I can't kick that user!");}
let options = new TagFilter([
new Tag(['r', 'reason'], 'reason', 'append')/*,
new Tag(['n', 'notes'], 'notes', 'append')*/
]).test(args.join(" "));
let reason;
if (options.reason && options.reason.length) {reason = options.reason;}
//if (options.notes && options.notes.length > 250) {return message.channel.send("Hey, listen, let's not write an essay on why you're kicking that member!");}
else {if (args[1]) {args.shift(); reason = args.join(" ");}}
if (reason && reason.length > 250) {return message.channel.send("Hey, listen, let's not write an essay on why you're kicking that member!");}
return user.kick(reason)
.then(async () => {
/*let mh = await Mod.findOne({gid: message.guild.id}) || new Mod({gid: message.guild.id});
let mhcases = mh.cases;
mhcases.push({
members: [user.id],
punishment: "Kicked",
reason: reason ? reason : "",
status: "Closed",
moderators: [message.author.id],
notes: options.notes,
history: [`${new Date().toISOString()} - ${message.author.username} - Created case`, `${new Date().toISOString()} - ${message.author.username} - Kicked ${client.users.cache.get(user.id).username}`],
issued: new Date().toUTCString()
});
mh.cases = mhcases;
mh.save();*/
return message.channel.send(`I got em outta here!${reason ? ` Reason for kicking: ${reason}` : ''}`);
})
.catch(() => {return message.channel.send("Something went wrong while trying to kick that user! If the problem persists, contact my devs.");});
}
};

@ -1,72 +0,0 @@
const Discord = require('discord.js');
const GuildData = require('../../models/guild');
const Responses = require('../../models/responses');
const sendResponse = require('../../util/response/sendresponse');
module.exports = {
name: "leave",
aliases: ['lv', 'leavemsg', 'leavemessage', 'leavechannel', 'lch', 'lmsg', 'leavech'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Leave Messages")
.setDescription("Set the channel and message for your leave messages!")
.addField("Syntax", "`leave <set|clear|view|test>`")
.addField("Notice", "You must be a staff or admin in your server to edit these settings.")
.addField("Responses", "Your leave message should be generated through a response using my `response` command, and then bound to the leave message by providing your response's name."),
meta: {
category: 'Moderation',
description: "Set the channel and message to be sent when a user leaves the server.",
syntax: '`leave <set|clear|view|test>`',
extra: "You must use the `response` command to create a response. The response's name is what will be given in this command."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This command is server-only.");}
let tg = await GuildData.findOne({gid: message.guild.id}) ? await GuildData.findOne({gid: message.guild.id}) : new GuildData({gid: message.guild.id});
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}welcome <set|clear|view|test>\``);}
if (['v', 'view', 'c', 'check'].includes(args[0].toLowerCase())) {}
if ((!tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You can't do that without staff or admin permissions, silly!");}
if (['s', 'set'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.reply("You need to specify a channel for your leave messages to be sent in!");}
let ch = message.mentions.channels.first() && args[1].match(/^<#(?:\d+)>$/) ? message.mentions.channels.first().id : message.guild.channels.cache.has(args[1]) ? message.guild.channels.cache.get(args[1]).id : null;
if (!ch) {return message.reply("I can't find that channel!");}
if (!message.guild.channels.cache.get(ch).permissionsFor(client.user.id).has("SEND_MESSAGES")) {return message.reply("I can't send messages in that channel. Try fixing the permissions or using a different channel!");}
if (!args[2]) {return message.reply(`You have to specify a response to use! You can make one with \`${prefix}response new\`.`);}
let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id});
if (!tr.responses.has(args[2].toLowerCase())) {return message.reply("Silly, I can't let you know that someone left with a response that doesn't exist! Try making one or make sure you spelled the name correctly.");}
tg.lch = ch;
tg.save();
tr.bindings.set('leave', args[2].toLowerCase());
tr.save();
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Leave Channel/Message Updated")
.setDescription(`This server's leave-notifying settings have been altered by ${message.author.tag}.\n\n**Channel**: <#${ch}>\n**Response Name**: \`${args[2].toLowerCase()}\``)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]})
}
if (['t', 'test'].includes(args[0].toLowerCase())) {
let tr = await Responses.findOne({gid: message.guild.id});
if (!tr || !tr.bindings.has('leave') || !tr.responses.has(tr.bindings.get('leave'))) {return message.reply("I can't test your leave message because the response doesn't exist, a leave response isn't set, or you haven't made any responses in this server.");}
await sendResponse(message.member, message.channel, 'this shit aint matter anymore lol', client, tr.responses.get(tr.bindings.get('leave')));
}
if (['clear'].includes(args[0].toLowerCase())) {
tg.lch = '';
tg.save();
let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id});
if (tr) {
tr.bindings.delete('leave');
tr.save();
}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Leave Channel/Message Updated")
.setDescription(`This server's leave-notifying settings have been altered by ${message.author.tag}.\n\n**Channel**: None`)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
}
};

@ -1,94 +0,0 @@
const Discord = require("discord.js");
const GuildData = require('../../models/guild');
const LogData = require('../../models/log');
const ObjLogTypes = {
mdelete: ['md', 'mdelete', 'messagedelete', 'deletemessage', 'deletemsg', 'msgdelete'],
medit: ['me', 'medit', 'messageedit', 'editmessage', 'msgedit', 'editmsg'],
ch: ['channel', 'ch'],
//chedit: ['channeledit'],
vc: ['voice', 'vc'],
//servervcmute: [],
//servervcdeafen: [],
//kick: [],
//ban: [],
//mute: [],
//warn: [],
//giverole: [],
//takerole: [],
//addrole: [],
//editrole: [],
//deleterole: [],
//serverjoin: [],
//serverleave: [],
//nickname: [],
//username: [],
//avatar: []
}; const LogTypes = new Map();
let keys = Object.keys(ObjLogTypes);
let key; for (key of keys) {let vs = ObjLogTypes[key]; let v; for (v of vs) {LogTypes.set(v, key);}}
module.exports = {
name: "logs",
aliases: ["log", "l", "modlog", "modlogs"],
help: new Discord.MessageEmbed()
.setTitle("Help -> Server Logs")
.setDescription("Configure your server's log settings.\n\nLogs will update you on events in your server that have the potential to require moderator intervention, like someone deleting a hateful message before you can see it or a misbehaving moderator kicking/banning a member when they aren't supposed to.")
.addField("Syntax", "`log <set|list|view|clear> [logType] [#channel]`")
.addField("Notice", "You must be an admin or have the specified staff role in order to use this command."),
meta: {
category: 'Moderation',
description: "Configure your server's log settings, which allow mods to see potentially suspicious activity in the server.",
syntax: '`log <set|list|view|clear> [logType] [#channel]`',
extra: "**Please note** that this command is still in the works, and that not all log types are available. The currently existing ones have been thoroughly tested, though."
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This command is server-only!");}
let tg = await GuildData.findOne({gid: message.guild.id});
if ((!message.member.permissions.has("ADMINISTRATOR")) && (!tg || !tg.staffrole || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole))) {return message.reply("You must be an administrator or have the specified staff role in this server in order to edit or view log settings.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}log <set|list|view|clear> [logType] [#channel]\``);}
if (['s', 'set'].includes(args[0].toLowerCase())) {
if (args.length < 3) {return message.channel.send(`You must specify the log type and the channel to send the log to. Use \`${prefix}log list\` to see a list of valid log types.`);}
if (!LogTypes.has(args[1].toLowerCase())) {return message.channel.send("That's not a valid log type. Use \`${prefix}log list\` to see a list of valid log types.");}
let lt = LogTypes.get(args[1].toLowerCase());
let ch = args[2].match(/<\#(?:\d+)>/m) && message.guild.channels.cache.has(message.mentions.channels.first().id) ? message.mentions.channels.first() : message.guild.channels.cache.has(args[2]) ? message.guild.channels.cache.get(args[2]) : null;
if (!ch) {return message.channel.send("I can't find that channel! Make sure that you've mentioned one, or that the ID you provided is correct, and that I can see it.");}
if (!ch.permissionsFor(client.user.id).has("SEND_MESSAGES")) {return message.reply("I don't have permissions to send messages in that channel. Please give me access and try again.");}
let tl = await LogData.findOne({gid: message.guild.id}) || new LogData({gid: message.guild.id});
tl.logs[lt] = ch.id;
tl.markModified(`logs.${lt}`);
tl.save();
if (!client.guildconfig.logs.has(message.guild.id)) {client.guildconfig.logs.set(message.guild.id, new Map());}
client.guildconfig.logs.get(message.guild.id).set(lt, ch.id);
return message.channel.send("Log settings updated!");
}
if (['l', 'list'].includes(args[0].toLowerCase())) {
return message.channel.send("Valid log types:\n\n-`msgdelete` - Shows the content of a message that was deleted, in any channel.\n-`msgedit` - Shows both the old and new versions of a message when it is edited.\n-`vc` - Logs when members join and leave VCs.\n-`ch` - Logs channel creations and deletions.");
}
if (['v', 'view'].includes(args[0].toLowerCase())) {
if (client.guildconfig.logs.has(message.guild.id) && client.guildconfig.logs.get(message.guild.id).size) {return message.channel.send(`This server's logs: \n\n${function bonk(){let s = ''; Array.from(client.guildconfig.logs.get(message.guild.id).keys()).forEach(v => s+=`\`${v}\`: <#${client.guildconfig.logs.get(message.guild.id).get(v)}>, `); return s;}().slice(0, -2)}`);}
else {return message.channel.send("Your server doesn't have any logs set up at the moment, or they aren't cached. If you keep seeing this issue even after setting logs, please contact my developers!");}
}
if (['c', 'clear'].includes(args[0].toLowerCase())) {
let tl = await LogData.findOne({gid: message.guild.id});
if (!tl) {return message.channel.send("Your server doesn't have any logs set up!");}
const conf = await require('../../util/ask')(message, "Are you sure you want to clear all your server's logs settings? This cannot be undone.", 60000);
if (!conf) {return;}
if (!['y', 'yes', 'sure'].includes(`${conf}`.toLowerCase())) {return message.channel.send("Okay, I won't clear your logs settings.");}
return LogData.deleteOne({gid: message.guild.id})
.then(() => {
client.guildconfig.logs.delete(message.guild.id);
return message.channel.send("Logs settings wiped!")
})
.catch(() => message.channel.send("An error occurred, and your logs data wasn't deleted. Please contact my devs if the problem persists."));
}
}
};

@ -1,85 +0,0 @@
const Discord = require('discord.js');
const GuildData = require('../../models/guild');
const Responses = require('../../models/responses');
const sendResponse = require('../../util/response/sendresponse');
const parseResponse = require('../../util/response/parseresponse');
const saveResponse = require('../../util/response/saveresponse');
const getResponse = require('../../util/response/getresponse');
module.exports = {
name: "response",
aliases: ['r', 'resp'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Responses")
.setDescription("Configure your server's saved responses. These are reusable and editable, and can be placed in things like welcome messages and used for announcements.")
.addField("Syntax", "`response <new|edit|view|list|delete|test|quick>`")
.addField("Notice", "You must have your server's staff role or be an admin to use this command."),
meta: {
category: 'Moderation',
description: "Set responses that can be used for various purposes in your server, namely welcome and leave messages.",
syntax: '`response <new|edit|view|list|delete|test|quick>`',
extra: "Response editing is currently not available and will be Soon:tm:"
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("You must be in a server to use this command.");}
let tg = await GuildData.findOne({gid: message.guild.id});
if (!['q', 'quick'].includes(args[0].toLowerCase()) && ((tg && tg.staffrole.length && !message.member.roles.cache.has(tg.staffrole))) && !message.member.permissions.has("ADMINISTRATOR")) {return message.reply("you need to be staff or admin in this server in order to edit those settings.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}response <new|edit|view|list|delete|test|quick>\``);}
if (args.length < 1) {return message.reply("You have to tell me what I'm supposed to find or save!");}
if (['q', 'quick'].includes(args[0].toLowerCase())) {return await sendResponse(message.member, message.channel, 'quick', client, await parseResponse(message, client, args));}
if (['n', 'new', 's', 'save'].includes(args[0].toLowerCase())) {return await saveResponse(await parseResponse(message, client, args), message);}
if (['t', 'test', 'send'].includes(args[0].toLowerCase())) {return await sendResponse(message.member, message.channel, 'quick', client, await getResponse(message, args[1]));}
if (['r', 'remove', 'd', 'delete', 'del'].includes(args[0].toLowerCase())) {
let tr = await Responses.findOne({gid: message.guild.id});
if (!tr) {return message.reply("This server has no responses for me to delete.");}
if (!tr.responses.has(args[1].toLowerCase())) {return message.reply("I can't find that response.");}
tr.responses.delete(args[1].toLowerCase());
let hadBinding = false;
let bm = '';
tr.bindings.forEach((v, k) => {if (v === args[1].toLowerCase()) {
tr.bindings.delete(v);
hadBinding = true;
bm += `This response was bound to \`${k}\`, so that has also been removed.\n`;
}});
tr.save();
return message.channel.send(`I removed the response \`${args[1].toLowerCase()}\`.${hadBinding ? `\n\n${bm}` : ''}`);
}
if (['list', 'l'].includes(args[0].toLowerCase())) {
let tr = await Responses.findOne({gid: message.guild.id});
if (!tr && !tr.responses.size) {return message.reply("This server has no responses for me to show you.");}
let s = "This server's response names: "; let resps = Array.from(tr.responses.keys());
let resp; for (resp of resps) {s += `\`${resp}\`${resps.indexOf(resp) !== resps.length - 1 ? ', ' : ''}`;}
return message.channel.send(s);
}
if (['view', 'v'].includes(args[0].toLowerCase())) {
let tr = await Responses.findOne({gid: message.guild.id});
if (!tr) {return message.reply("I'd give you information on a response, but this server doesn't have any.");}
if (!tr.responses.has(args[1].toLowerCase())) {return message.reply("I can't find that response.");}
let hasBinding = false;
let bm = '';
tr.bindings.forEach((v, k) => {if (v === args[1].toLowerCase()) {hasBinding = true; bm += !bm.length ? `\`${k}\`` : `, \`${k}\``}});
let infoEmbed = new Discord.MessageEmbed()
.setTitle("Response Info")
.setDescription(`Requested by ${message.member.displayName}`)
.addField("Name/ID", args[1].toLowerCase(), true)
.addField("Type", tr.responses.get(args[1].toLowerCase()).embed ? "Embed" : "Message", true)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp();
if (hasBinding) {infoEmbed.addField("Server Bindings", bm);}
return message.channel.send({embeds: [infoEmbed]});
}
if (['edit', 'e', 'm', 'modify'].includes(args[0].toLowerCase())) {
let options = await getResponse(message, args[1]);
if (!options) {return;}
}
return message.channel.send("Invalid argument! (Or it's one that isn't supported yet. Sorry.)");
return message.channel.send(`Syntax: \`${prefix}response <new|edit|view|list|delete|test|quick>\``);
}
};

@ -1,72 +0,0 @@
const Discord = require('discord.js');
const {Tag} = require("../../util/tag");
const {TagFilter} = require("../../util/tagfilter");
module.exports = {
name: "softban",
aliases: ['falseban', 'sfb'],
meta: {
category: 'Moderation',
description: "Bans a user from the server, deletes their messages, then unbans them",
syntax: '`softban <@user|userID> [reason]`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> SoftBan")
.setDescription("Bans a user from the server and deletes their messages, then unbans them. This is a great way to kick a member from the server while getting the message delete effect of a ban.")
.addField("Syntax", "`softban <@user|userID> [reason]`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}softban <@user|userID> [reason]\``);}
if (!message.member.permissions.has("BAN_MEMBERS")) {return message.channel.send("You don't have permissions to do that!");}
if (!message.guild.me.permissions.has("BAN_MEMBERS")) {return message.channel.send("I don't have permissions to ban members in your server.");}
let user = message.guild.members.cache.get(args[0]) || message.mentions.members.first();
if (!user) {return message.channel.send("You must mention a user to softban, or provide their ID.");}
if (user.roles.highest.position >= message.member.roles.highest.position) {return message.channel.send("You don't have permissions to softban that member as they are above you in the roles list.");}
if (user.roles.highest.position >= message.guild.me.roles.highest.position) {return message.channel.send("I can't ban that member as their highest role is above mine! (Or the same as mine, too)");}
if (!user.bannable) {return message.channel.send("Hmm, it seems like I can't ban that member. This is probably a permissions issue. Or maybe they were already banned? In that case, just use `unban`");}
let options = new TagFilter([
new Tag(['r', 'reason'], 'reason', 'append'),
new Tag(['n', 'notes'], 'notes', 'append'),
new Tag(['d', 'days', 'm', 'messages'], 'days', 'append')
]).test(args.join(" "));
let reason; let days;
if (options.reason && options.reason.length) {reason = options.reason;}
if (options.days && options.days.length) {
if (isNaN(Number(options.days)) || Number(options.days) < 1 || Number(options.days) > 7 || options.days.includes('.')) {return message.channel.send("The `days` option must be a whole number between 1 and 7.");}
days = Number(options.days);
} else {days = 7;}
if (options.notes && options.notes.length > 250) {return message.channel.send("I mean I get it, they pissed you off, but do you really need to give me that much info on why you're softbanning them? I can't keep track of all that!");}
else {if (args[1] && !options.days /*&& (!options.notes || !options.notes.length)*/ && (!options.reason || !options.reason.length)) {args.shift(); reason = args.join(" ");}}
if (reason && reason.length > 250) {return message.channel.send("I mean I get it, they pissed you off, but do you really need to give me that much info on why you're softbanning them? I can't keep track of all that!");}
return user.ban({reason: reason, days: days})
.then(async () => {
/*let mh = await Mod.findOne({gid: message.guild.id}) || new Mod({gid: message.guild.id});
let mhcases = mh.cases;
mhcases.push({
members: [user.id],
punishment: "Banned",
reason: reason ? reason : "",
status: "Closed",
moderators: [message.author.id],
notes: options.notes,
history: [`${new Date().toISOString()} - ${message.author.username} - Created case`, `${new Date().toISOString()} - ${message.author.username} - Banned ${client.users.cache.get(user.id).username}`],
issued: new Date().toUTCString()
});
mh.cases = mhcases;
mh.save();*/
return message.guild.members.unban(user.id, reason)
.then(async () => {return message.channel.send("That user has been softbanned, and can now be re-invited to the server.");})
.catch(() => {return message.channel.send("Something went wrong while trying to unban that user! This means that the member has been banned, but not unbanned afterward, so you'll need to unban them using the `unban` command or by doing it manually. If the problem persists, contact my devs.");});
})
.catch(() => {return message.channel.send("Something went wrong while trying to ban that user! If the problem persists, contact my devs.");});
}
};

@ -1,55 +0,0 @@
const Discord = require('discord.js');
const mongoose = require('mongoose');
const GuildSettings = require('../../models/guild');
module.exports = {
name: "staffrole",
aliases: ['sr', 'setstaffrole'],
help: "Set your server's staff role, which allows users with that role to modify my settings in this server. You must be an admin in the server to change this setting.",
meta: {
category: 'Moderation',
description: "Set the role that can edit my settings for the server",
syntax: '`staffrole <@role|roleID|clear|view>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This is a guild-only command!");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}staffrole <@role|roleID|clear|view>\``);}
if (!message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You must be an admin in this server in order to change this setting!");}
let tguild = await GuildSettings.findOne({gid: message.guild.id})
? await GuildSettings.findOne({gid: message.guild.id})
: new GuildSettings({gid: message.guild.id});
if (['view', 'v'].includes(args[0].trim().toLocaleLowerCase())) {return message.reply(
tguild.staffrole.length
? message.guild.roles.cache.has(tguild.staffrole)
? `people with the \`${message.guild.roles.cache.get(tguild.staffrole).name}\` role can edit my setting here.`
: `I have a role stored for this server, but it doesn't seem to exist anymore, so only admins can edit my settings right now.`
: 'only admins may edit settings in this server.'
);}
let role = !['c', 'clear', 'n', 'none'].includes(args[0].trim().toLowerCase()) ? message.mentions.roles.size ? message.mentions.roles.first() : message.guild.roles.cache.has(args[0]) ? message.guild.roles.cache.get(args[0]) : null : 'c';
if (!role) {return message.reply("I couldn't find that role!");}
if (role === "c") {
tguild.staffrole = '';
tguild.save();
return message.reply("got it, only admins can edit my settings in this server.");
} else {
tguild.staffrole = role.id;
tguild.save();
let upm = await message.reply("sure thing!");
await require('../../util/wait')(1750);
return upm.edit({embeds: [new Discord.MessageEmbed()
.setAuthor({name: 'Staff role updated!', iconURL: message.author.displayAvatarURL()})
.setDescription(`<@&${tguild.staffrole}> can now edit my settings in this server.`)
.addField('Auditing Admin', `<@${message.member.id}>`, true)
.addField('Role-Holders', `${message.guild.members.cache.filter(m => m.roles.cache.has(tguild.staffrole) && !client.users.cache.get(m.id).bot).size}+ members have this role`, true)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
}
};

@ -1,27 +0,0 @@
const Discord = require('discord.js');
const GuildSettings = require('../../models/guild');
module.exports = {
name: "togglestatuses",
aliases: ['ts', 'tsw', 'togglestatuswarnings', 'togglestatus', 'statustoggle', 'statusestoggle'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Server Status-Toggling")
.setDescription("Disables or enables the warning that appears when you ping someone that has a status set.")
.addField("Syntax", "`togglestatuses [c]` (add `c` to the end of the message if you want to check if they're enabled or not.)"),
meta: {
category: 'Moderation',
description: "Toggle the warning I give members when they ping someone with a status. Some people find it annoying, but here's my mute button!",
syntax: '`togglestatuses [c]`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply('You must be in a server to use this command.');}
let tg = await GuildSettings.findOne({gid: message.guild.id});
if ((!tg || !tg.staffrole || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You must be a staff member of the server or have Administrator permissions in order to use this command.");}
if (args[0] && ['c', 'check', 'v', 'view'].includes(args[0].toLowerCase())) {return message.channel.send(`I ${tg && !tg.nostatus ? 'will' : 'will not'} send a warning when pinging a member with a status.`);}
if (!tg) {tg = new GuildSettings({gid: message.guild.id});}
tg.nostatus = !tg.nostatus;
tg.save();
return message.channel.send(`I ${!tg.nostatus ? 'will' : 'will not'} send a warning when pinging a member with a status.`);
}
};

@ -1,33 +0,0 @@
const Discord = require('discord.js');
module.exports = {
name: "unban",
aliases: ['ub'],
meta: {
category: 'Moderation',
description: "Unban a user from the server",
syntax: '`unban <userID|@user>`',
extra: null,
guildOnly: true
},
help: new Discord.MessageEmbed()
.setTitle("Help -> Unban")
.setDescription("Unbans a user from the server, allowing them to join again if they have an invite.")
.addField("Syntax", "`unban <userID|@user>`"),
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}unban <userID|@user>\``);}
if (!message.member.permissions.has("BAN_MEMBERS")) {return message.channel.send("You don't have permissions to do that!");}
if (!message.guild.me.permissions.has("BAN_MEMBERS")) {return message.channel.send("I don't have permissions to unban members in your server.");}
let user = client.users.cache.get(args[0]) || message.mentions.users.first();
if (!user) {
user = await client.users.fetch(args[0]);
if (!user) {return message.channel.send("You must mention a user to unban, or provide their ID.");}
}
return message.guild.members.unban(user.id)
.then(async () => {return message.channel.send("I've unbanned that user!");})
.catch(() => {return message.channel.send("Something went wrong while trying to unban that user! If the problem persists, contact my devs.");});
}
};

@ -1,140 +0,0 @@
const Discord = require('discord.js');
const Mod = require('../../models/mod');
const {TagFilter} = require('../../util/tagfilter');
const {Tag} = require('../../util/tag');
module.exports = {
name: "warn",
help: new Discord.MessageEmbed()
.setTitle("Help -> Warnings")
.setDescription("Warn misbehaving members that what they are doing is wrong, and have it stored in a database in order to see a list of all their past warnings")
.addField("Syntax", "`warn <@member> <warningMessage|check|clear>`")
.addField("Notice", "You must be a server administrator in order to use this command."),
meta: {
category: 'Moderation',
description: "Warn misbehaving members that what they are doing is wrong, and have it stored in a database in order to see a list of all their past warnings",
syntax: '`warn <@member> <warningMessage|check|clear>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.channel.send("This is a server-only command.");}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}warn <@member> <warningMessage|check|clear>\``);}
if (!message.member.permissions.has("MANAGE_MESSAGES") && !message.member.permissions.has("MANAGE_GUILD")) {return message.reply("You must be a server moderator (manage messages or manage server permissions) to use this command.");}
if (args.length < 2 && !['check', 'c', 'list', 'l', 'clear', 'e', 'empty'].includes(args[0].toLowerCase())) {return message.channel.send("You must provide a reason for warning the user, or `check` or `clear`.");}
let user = message.mentions.members.first() && args[0].match(/^<@!?\d+>$/) ? message.mentions.members.first() : message.guild.members.cache.has(args[0]) ? message.guild.members.cache.get(args[0]) : null;
if (!user && args.length > 1) {return message.channel.send("Either you didn't mention a user, or I can't find that user!");}
if (args.length > 1) {args.shift();}
if (['check', 'c', 'list', 'l'].includes(args[0].toLowerCase())) {
user = user ? user : message.member;
let mh = await Mod.findOne({gid: message.guild.id});
if (!mh || !Object.keys(mh.warnings).length) {return message.reply("There are no warnings available in this server.");}
if (!mh.warnings[user.id] || !mh.warnings[user.id].length) {return message.reply(`${user.id === message.author.id ? 'You have' : 'That user has'} never been warned in this server.`);}
//console.log(mh.cases, mh.warnings);
let ws = '';
let cwc = 0;
let warning; for (warning of mh.warnings[user.id]) {
let tcase = mh.cases[warning - 1];
if (tcase.status !== "Cleared") {
ws += `\`Case #${warning}\` - Issued by <@${tcase.moderators[0]}>\n${tcase.reason}\n\n`;
} else {cwc++;}
}
if (cwc > 0) {ws += '*Plus ' + cwc + ' other warnings that have been cleared.*';}
if (cwc === mh.warnings[user.id].length) {return message.reply("That user has no uncleared warnings.");}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("User Warnings")
.setThumbnail(client.users.cache.get(user.id).displayAvatarURL({size: 1024}))
.setDescription(`For ${user.displayName}`)
.addField("Warnings", ws)
.setColor("c375f0")
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
else if (['clear', 'e', 'empty'].includes(args[0].toLowerCase())) {
user = user ? user : message.member;
let mh = await Mod.findOne({gid: message.guild.id});
if (!mh || !Object.keys(mh.warnings).length) {return message.reply("There are no warnings available in this server.");}
if (!Object.keys(mh.warnings).includes(user.id) || !mh.warnings[user.id].length) {return message.reply(`${user.id === message.author.id ? 'You have' : 'That user has'} never been warned in this server.`);}
let mhcases = mh.cases;
let moddedcases = [];
let cwc = 0; var wc = 0;
let warning; for (warning of mh.warnings[user.id]) {
if (mhcases[`${warning - 1}`].status !== "Cleared") {
let tcase = mhcases[`${warning - 1}`];
tcase.status = "Cleared";
tcase.history.push(`${new Date().toISOString()} - ${message.author.username} - Cleared the warning.`);
moddedcases.push(`${warning - 1}`);
wc++;
if (!tcase.moderators.includes(message.author.id)) {tcase.moderators.push(message.author.id);}
mhcases[`${warning - 1}`] = tcase;
} else {cwc++;}
}
if (cwc === mh.warnings[user.id].length) {return message.reply("That user has no uncleared warnings.");}
if (moddedcases.length) {let c; for (c of moddedcases) {mh.markModified(`cases.${c}.history`);}}
mh.cases = mhcases;
mh.save();
return message.reply(`Cleared ${wc} warnings from ${user.displayName}.`);
}
else {
if (user.id === message.author.id) {return message.channel.send("You can't warn yourself!");}
if (user.id === client.user.id) {return message.channel.send("You can't warn me, silly! What do you want me to do, spank myself?");}
if (client.users.cache.get(user.id).bot) {return message.channel.send("You can't warn a bot!");}
let options = new TagFilter([
new Tag(['r', 'reason'], 'reason' ,'append'),
new Tag(['nd', 'nm', 'nomessage', 'nodm'], 'nodm' ,'toggle'),
new Tag(['silent', 's'], 'silent', 'toggle'),
new Tag(['note', 'n', 'notes'], 'notes', 'append')
]).test(args.join(" "));
if (Object.keys(options).length && (!options.reason || !options.reason.length)) {return message.channel.send("You *must* use -r or -reason to specify your reason if you include any other tags. Please try again!");}
let reason = options.reason && options.reason.length ? options.reason : args.join(" ");
if (reason.length > 200) {return message.reply("Please keep your reason short and sweet - less than 200 characters, please!");}
if (options.notes && options.notes.length > 150) {return message.reply("Please keep your notes short and sweet - less than 150 characters, please!");}
let notes = options.notes && options.notes.length ? [options.notes] : [];
let mh = await Mod.findOne({gid: message.guild.id}) || new Mod({gid: message.guild.id});
let mhcases = mh.cases;
mhcases.push({
members: [user.id],
punishment: "Warned",
reason: reason,
status: "Closed",
moderators: [message.author.id],
notes: notes,
history: [`${new Date().toISOString()} - ${message.author.username} - Created case`, `${new Date().toISOString()} - ${message.author.username} - Warned ${client.users.cache.get(user.id).username}`],
issued: new Date().toUTCString()
});
let mhwarnings = mh.warnings;
let mhwarningsk = Object.keys(mhwarnings);
if (mhwarningsk.includes(user.id)) {let tw = mhwarnings[user.id]; tw.push(mhcases.length); mhwarnings[user.id] = tw;}
else {mhwarnings[user.id] = [mhcases.length];}
mh.warnings = mhwarnings;
mh.warnings[user.id] = mhwarnings[user.id];
mh.cases = mhcases;
if (!options.silent) {message.channel.send(`Case ${mh.cases.length} - Member has been warned. Reason: \`${reason}\``);}
if (!options.silent && !options.nodm) {client.users.cache.get(user.id).send(`\`${message.author.username}\` has warned you in \`${message.guild.name}\`. Reason: **${reason}**`);}
mh.markModified(`warnings.${user.id}`);
mh.save();
return null;
}
}
};

@ -1,72 +0,0 @@
const Discord = require('discord.js');
const GuildData = require('../../models/guild');
const Responses = require('../../models/responses');
const sendResponse = require('../../util/response/sendresponse');
module.exports = {
name: "welcome",
aliases: ['wel', 'welcomemsg', 'welcomemessage', 'welcomechannel', 'wch', 'wmsg', 'welcomech'],
help: new Discord.MessageEmbed()
.setTitle("Help -> Welcome Messages")
.setDescription("Set the channel and message for your welcome messages!")
.addField("Syntax", "`welcome <set|clear|view|test>`")
.addField("Notice", "You must be a staff or admin in your server to edit these settings.")
.addField("Responses", "Your welcome message should be generated through a response using my `response` command, and then bound to the welcome message by providing your response's name."),
meta: {
category: 'Moderation',
description: "Set the channel and message to be sent when a user joins the server.",
syntax: '`welcome <set|clear|view|test>`',
extra: "You'll need to use `response` to configure the message that you want sent with this command. The name you give the response is what you'll give to this command"
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!message.guild) {return message.reply("This command is server-only.");}
let tg = await GuildData.findOne({gid: message.guild.id}) ? await GuildData.findOne({gid: message.guild.id}) : new GuildData({gid: message.guild.id});
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}welcome <set|clear|view|test>\``);}
if (['v', 'view', 'c', 'check'].includes(args[0].toLowerCase())) {}
if ((!tg || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You can't do that without staff or admin permissions, silly!");}
if (['s', 'set'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.reply("You need to specify a channel for your welcome messages to be sent in!");}
let ch = message.mentions.channels.first() && args[1].match(/^<#(?:\d+)>$/) ? message.mentions.channels.first().id : message.guild.channels.cache.has(args[1]) ? message.guild.channels.cache.get(args[1]).id : null;
if (!ch) {return message.reply("I can't find that channel!");}
if (!message.guild.channels.cache.get(ch).permissionsFor(client.user.id).has("SEND_MESSAGES")) {return message.reply("I can't send messages in that channel. Try fixing the permissions or using a different channel!");}
if (!args[2]) {return message.reply(`You have to specify a response to use! You can make one with \`${prefix}response new\`.`);}
let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id});
if (!tr.responses.has(args[2].toLowerCase())) {return message.reply("Silly, I can't welcome someone with a response that doesn't exist! Try making one or make sure you spelled the name correctly.");}
tg.wch = ch;
tg.save();
tr.bindings.set('welcome', args[2].toLowerCase());
tr.save();
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Welcome Channel/Message Updated")
.setDescription(`This server's member-welcoming settings have been altered by ${message.author.tag}.\n\n**Channel**: <#${ch}>\n**Response Name**: \`${args[2].toLowerCase()}\``)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]})
}
if (['t', 'test'].includes(args[0].toLowerCase())) {
let tr = await Responses.findOne({gid: message.guild.id});
if (!tr || !tr.bindings.has('welcome') || !tr.responses.has(tr.bindings.get('welcome'))) {return message.reply("I can't test your welcome message because the response doesn't exist, a welcome response isn't set, or you haven't made any responses in this server.");}
await sendResponse(message.member, message.channel, 'this shit aint matter anymore lol', client, tr.responses.get(tr.bindings.get('welcome')));
}
if (['clear'].includes(args[0].toLowerCase())) {
tg.wch = '';
tg.save();
let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id});
if (tr) {
tr.bindings.delete('welcome');
tr.save();
}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle("Welcome Channel/Message Updated")
.setDescription(`This server's member-welcoming settings have been altered by ${message.author.tag}.\n\n**Channel**: None`)
.setColor('c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
}
};

@ -1,50 +0,0 @@
const Discord = require('discord.js');
const mongoose = require('mongoose');
const UserData = require('../../models/user');
module.exports = {
name: "afk",
aliases: ['setafk'],
help: new Discord.MessageEmbed()
.setTitle("Help -> AFK")
.setDescription("Set your status within the bot as AFK and specify a reason. Then, when other people ping you, I can let them know that you're not available!")
.addField("Syntax", "`afk [clearMode] <reason>`")
.addField("Notice","Your status clear mode can be set to either 'auto' or 'manual'. If not specified, it will clear next time you send a message (auto)."),
meta: {
category: 'Social',
description: "Tell others that you're AFK so that they'll be notified when you ping them.",
syntax: '`afk [clearMode] <reason>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
args = msg.startsWith(prefix)
? message.content.slice(prefix.length).trim().split(/ +/g)
: msg.startsWith('<@!')
? message.content.slice(4 + client.user.id.length).trim().split(/ +/g)
: message.content.slice(3 + client.user.id.length).trim().split(/ +/g);
args = args.slice(1);
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}afk [clearMode] <reason>\``);}
let tu = await UserData.findOne({uid: message.author.id})
? await UserData.findOne({uid: message.author.id})
: new UserData({uid: message.author.id});
if (['m', 'manual', 'a', 'auto'].includes(args[0])) {
tu.statusclearmode = ['m', 'manual'].includes(args[0]) ? 'manual' : 'auto';
args.shift();
} else {tu.statusclearmode = 'auto';}
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}afk [clearMode] <reason>\``);}
let reason = args.join(" ");
if (reason.length > 150) {return message.channel.send("That status a bit long; keep it under 150 characters.");}
if (reason.match(/<@&\d+>|@everyone/gm)) {return message.channel.send("I won't ping any roles or @ everyone!");}
if (reason.split(/\n/gm).length > 10) {return message.channel.send("That's too many lines!");}
tu.statustype = 'afk';
tu.statusmsg = reason.trim();
tu.statussetat = new Date();
let tempDate = new Date();
tu.statusclearat = tempDate.setHours(tempDate.getHours() + 12);
tu.markModified("statussetat");
tu.markModified("statusclearat");
tu.save();
require('../../util/cachestatus')(message.author.id, tempDate.setHours(tempDate.getHours() + 12));
return message.channel.send(`I set your ${tu.statusclearmode === 'auto' ? 'automatically' : 'manually'}-clearing AFK message to: ${reason.trim()}`);
}
};

@ -1,38 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const makeId = require('../../util/makeid');
module.exports = {
name: "angry",
aliases: ['pissed', 'angry', 'mad', 'angy'],
help: "Show your anger with {{p}} angy",
meta: {
category: 'Social',
description: "Show the world how mad you are!",
syntax: '`angry`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'angry'}) ? await Saves.findOne({name: 'angry'}) : new Saves({name: 'angry'});
let saves = savess.saves;
if (!args.length) {return message.channel.send(new Discord.MessageEmbed()
.setTitle(`${message.guild ? message.member.displayName : message.author.username} is pissed off!`)
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('a21a1a')
);}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staff) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Staff member in order to add new angry GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("Save added!");
}
}
};

@ -1,61 +0,0 @@
const Discord = require('discord.js');
const UserData = require('../../models/user');
module.exports = {
name: "bio",
help: new Discord.MessageEmbed()
.setTitle("Help -> Bio")
.setDescription("Set and view user bios, which are fun ways to express yourself!")
.addField("Syntax", "`bio <set|view|clear>`"),
meta: {
category: 'Social',
description: "Set your own user bio, which can be seen by everyone!",
syntax: '`bio <set|view|clear>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
if (!args.length) {return message.channel.send(`Syntax: \`${prefix}bio <set|view|clear>\``);}
let tu = await UserData.findOne({uid: message.author.id}) ? await UserData.findOne({uid: message.author.id}) : new UserData({uid: message.author.id});
if (['v', 'view', 'check'].includes(args[0].toLowerCase())) {
let person = args[1] ? args[1].match(/^<@!?\d+>$/) && message.mentions.users.first() ? message.mentions.users.first().id : message.guild && message.guild.members.cache.has(args[1]) ? args[1] : message.author.id : message.author.id;
let pud = await UserData.findOne({uid: person});
if (!pud || !pud.bio || !pud.bio.length) {return message.reply(person === message.author.id ? "You don't have a bio set!" : "That user has no bio for me to show you!");}
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`Bio for ${message.guild ? message.guild.members.cache.get(person).displayName : message.author.username}`)
.setThumbnail(client.users.cache.get(person).displayAvatarURL({size: 2048}))
.setDescription(pud.bio)
.setColor(pud.color && pud.color.length ? pud.color : 'c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
if (['s', 'set'].includes(args[0].toLowerCase())) {
args.shift();
if (!args.length) {return message.reply("Please specify a bio!");}
let args2 = msg.startsWith(prefix)
? message.content.slice(prefix.length).trim().split(/ +/g)
: msg.startsWith('<@!')
? message.content.slice(4 + client.user.id.length).trim().split(/ +/g)
: message.content.slice(3 + client.user.id.length).trim().split(/ +/g);
args2.shift(); args2.shift();
let bio = args2.join(" ");
if (bio.length > 200) {return message.reply("Please keep your bio under 200 characters!");}
tu.bio = bio;
tu.save();
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`Bio Set!`)
.setThumbnail(message.author.displayAvatarURL({size: 2048}))
.setDescription(tu.bio)
.setColor(tu.color && tu.color.length ? tu.color : 'c375f0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()
]});
}
if (['c', 'clear'].includes(args[0].toLowerCase())) {
tu.bio = '';
tu.save();
return message.reply("Bio cleared!");
}
}
};

@ -1,37 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const makeId = require('../../util/makeid');
module.exports = {
name: "blush",
help: "Let others know that someone made your cheeks red `{{p}}sip`.",
meta: {
category: 'Social',
description: "Let others know that someone made your cheeks red",
syntax: '`blush`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'blush'}) ? await Saves.findOne({name: 'blush'}) : new Saves({name: 'blush'});
let saves = savess.saves;
if (!args.length) {return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`${message.guild ? message.member.displayName : message.author.username} blushes UwU`)
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('ad0072')
]});}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staff) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Staff member in order to add new sip GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("Save added!");
}
}
};

@ -1,62 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const VC = require('../../models/vscount');
const makeId = require('../../util/makeid');
module.exports = {
name: "bonk",
help: "Give someone a healthy bonk with `{{p}}bonk`!",
meta: {
category: 'Social',
description: "Give someone a good bonking!",
syntax: '`bonk <@user>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'bonk'}) ? await Saves.findOne({name: 'bonk'}) : new Saves({name: 'bonk'});
let saves = savess.saves;
if (!args.length) {
return message.channel.send(message.guild ? {embeds: [new Discord.MessageEmbed()
.setTitle(`${message.guild ? message.member.displayName : message.author.username} needs a good bonk!`)
.setThumbnail(message.author.displayAvatarURL({size: 2048}))
.setDescription(`Give them one with \`${prefix}bonk @${message.member.displayName}\`!`)
.setColor('dda0dd')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()]}
: "I fucking dare you to hit me."
);}
if (mention && args[0].match(/^<@!?\d+>$/)) {
if (!message.guild) {return message.reply("I dare you to hit me.");}
if (!message.guild.members.cache.has(mention.id)) {return message.reply("You missed your bonk..");}
if (message.author.id === mention.id) {return message.reply("I should bonk you for attempting to self harm.");}
let bonk = await VC.findOne({uid: message.author.id, countOf: 'bonk'}) || new VC({uid: message.author.id, countOf: 'bonk'});
bonk.against[mention.id] = bonk.against[mention.id] ? bonk.against[mention.id] + 1 : 1;
bonk.total++;
bonk.markModified(`against.${mention.id}`);
bonk.save();
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: `${message.guild ? message.member.displayName : message.author.username} bonks ${message.guild.members.cache.get(mention.id).displayName}!...ouch! `, iconURL: message.author.displayAvatarURL()})
.setDescription(`You've bonked them **${bonk.against[mention.id] === 1 ? 'once' : `${bonk.against[mention.id]} times!`}**`)
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('dda0dd')
.setFooter({text: `${bonk.total} bonk${bonk.total === 1 ? '' : 's'} total`})
]});
}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staff) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Staff member in order to add new bonk GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("Ayy new ways to bonk people");
}
}
};

@ -1,26 +0,0 @@
const Discord = require('discord.js');
const mongooes = require('mongoose');
const UserData = require('../../models/user');
module.exports = {
name: "clearstatus",
aliases: ['statusclear', 'cs'],
help: "Clears your status, if you have one set. Does not take any arguments.",
meta: {
category: 'Social',
description: "Clear your status, if you have one set.",
syntax: '`clearstatus`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let tu = await UserData.findOne({uid: message.author.id});
if (!tu && !tu.statusmsg.length) {return message.reply("you have no status for me to clear");}
if (tu.statusclearmode === "auto") {return;}
tu.statusmsg = '';
tu.statustype = '';
tu.save();
require('../../util/siftstatuses')(client, message.author.id, true);
return message.reply("welcome back! I cleared your status.").then(m => {setTimeout(() => m.delete(), 5000); if (message.guild && message.guild.me.permissions.has("DELETE_MESSAGES")) {message.delete().catch(() => {});}});
}
};

@ -1,56 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const makeId = require('../../util/makeid');
module.exports = {
name: "creampie",
help: "Show the world your creaming skils with `{{p}}creampie @person`!",
aliases: ['creampie'],
meta: {
category: 'Social',
description: "Give the gift of a creampie!",
syntax: '`Creampie <@user>`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'creampie'}) ? await Saves.findOne({name: 'creampie'}) : new Saves({name: 'creampie'});
let saves = savess.saves;
if (!args.length) {
let name = message.guild ? message.member.displayName : message.author.username;
return message.channel.send(message.guild ? {embeds: [new Discord.MessageEmbed()
.setTitle(`${name} needs a creampie!`)
.setThumbnail(message.author.displayAvatarURL({size: 2048}))
.setDescription(`Help with their..ahem..problem..with \`${prefix}creampie @${name}\`!`)
.setColor('fffdd0')
.setFooter({text: "Natsuki", iconURL: client.user.displayAvatarURL()})
.setTimestamp()]}
: "Do. Not. Touch. Me."
);}
if (mention && args[0].match(/^<@!?\d+>$/)) {
if (!message.guild) {return message.reply("No means no.");}
if (!message.guild.members.cache.has(mention.id)) {return message.reply("I guess they didn't want your creampie..");}
if (message.author.id === mention.id) {return message.reply("You can't give yourself a creampie..weirdo.");}
let name = message.guild ? message.member.displayName : message.author.username;
let uname = message.guild.members.cache.get(mention.id).displayName;
return message.channel.send({embeds: [new Discord.MessageEmbed()
.setAuthor({name: `${message.guild ? message.member.displayName : message.author.username} gives a massive creampie to ${message.guild.members.cache.get(mention.id).displayName}..Tasty!`, iconURL: message.author.displayAvatarURL()})
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('fffdd0')
]});
}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.developer) && !client.developers.includes(message.author.id) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Developer in order to add new creampie GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("I made a new creampie!...ew");
}
}
};

@ -1,38 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const makeId = require('../../util/makeid');
module.exports = {
name: "cry",
aliases: ['sob'],
help: "Tell others that you're crying with `{{p}}cry`. We're here for you!",
meta: {
category: 'Social',
description: "Tell others that you're not feeling so well using this command.",
syntax: '`cry`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'cry'}) ? await Saves.findOne({name: 'cry'}) : new Saves({name: 'cry'});
let saves = savess.saves;
if (!args.length) {return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`${message.guild ? message.member.displayName : message.author.username} is Crying!`)
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('8d42f5')
]});}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staff) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Staff member in order to add new cry GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("Save added!");
}
}
};

@ -1,38 +0,0 @@
const Discord = require('discord.js');
const Saves = require('../../models/saves');
const UserData = require('../../models/user');
const makeId = require('../../util/makeid');
module.exports = {
name: "dance",
aliases: ['party'],
help: "Shake it off with {{p}}dance!",
meta: {
category: 'Social',
description: "Show off your moves",
syntax: '`dance`',
extra: null
},
async execute(message, msg, args, cmd, prefix, mention, client) {
let savess = await Saves.findOne({name: 'dance'}) ? await Saves.findOne({name: 'dance'}) : new Saves({name: 'dance'});
let saves = savess.saves;
if (!args.length) {return message.channel.send({embeds: [new Discord.MessageEmbed()
.setTitle(`${message.guild ? message.member.displayName : message.author.username} is dancing like there's no tomorrow!`)
.setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)]))
.setColor('3A8ED5')
]});}
if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) {
if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');}
let tu = await UserData.findOne({uid: message.author.id});
if ((!tu || !tu.staff) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Staff member in order to add new dance GIFs.");}
let e = true;
let id;
while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}}
args.shift();
saves.set(id, args.join(" ").trim());
savess.saves = saves;
savess.save();
return message.channel.send("Save added!");
}
}
};

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save