Archived
0
0
Fork 0

Compare commits

..

10 commits

49 changed files with 5865 additions and 1999 deletions

1
.gitattributes vendored
View file

@ -1,2 +1 @@
.yarn/* linguist-vendored .yarn/* linguist-vendored
.pnp.cjs linguist-vendored

5
.idea/.gitignore vendored
View file

@ -1,5 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

View file

@ -1,12 +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" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,194 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</value>
</option>
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<option name="SOFT_MARGINS" value="80,120" />
<CssCodeStyleSettings>
<option name="HEX_COLOR_UPPER_CASE" value="true" />
<option name="HEX_COLOR_LONG_FORMAT" value="true" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="ENFORCE_QUOTES_ON_FORMAT" value="true" />
</CssCodeStyleSettings>
<DBN-PSQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false" />
</DBN-PSQL>
<DBN-SQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false">
<option name="STATEMENT_SPACING" value="one_line" />
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
</formatting-settings>
</DBN-SQL>
<HTMLCodeStyleSettings>
<option name="HTML_UNIFORM_INDENT" value="true" />
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<Markdown>
<option name="MAX_LINES_AROUND_HEADER" value="0" />
<option name="MAX_LINES_AROUND_BLOCK_ELEMENTS" value="0" />
<option name="MAX_LINES_BETWEEN_PARAGRAPHS" value="0" />
</Markdown>
<ScssCodeStyleSettings>
<option name="HEX_COLOR_UPPER_CASE" value="true" />
<option name="HEX_COLOR_LONG_FORMAT" value="true" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="ENFORCE_QUOTES_ON_FORMAT" value="true" />
</ScssCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_PUBLIC_MODIFIER" value="true" />
<option name="PREFER_AS_TYPE_CAST" value="true" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
<option name="IMPORT_SORT_MODULE_NAME" value="true" />
</TypeScriptCodeStyleSettings>
<codeStyleSettings language="CSS">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<NAME>.*</NAME>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="HTML">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Markdown">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SASS">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SCSS">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<NAME>.*</NAME>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Vue">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

View file

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View file

@ -1,6 +0,0 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="/*&#10; * This file is part of ArgonBot&#10; *&#10; * ArgonBot is free software: you can redistribute it and/or modify&#10; * it under the terms of the GNU General Public License as published by&#10; * the Free Software Foundation, either version 3 of the License, or&#10; * (at your option) any later version.&#10; *&#10; * ArgonBot is distributed in the hope that it will be useful,&#10; * but WITHOUT ANY WARRANTY; without even the implied warranty of&#10; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10; * GNU General Public License for more details.&#10; *&#10; * You should have received a copy of the GNU General Public License&#10; * along with ArgonBot. If not, see &lt;https: //www.gnu.org/licenses/&gt;.&#10; */" />
<option name="myName" value="GPL-3.0-or-later" />
</copyright>
</component>

View file

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View file

@ -1,13 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<list size="7">
<item index="0" class="java.lang.String" itemvalue="INFORMATION" />
<item index="1" class="java.lang.String" itemvalue="TYPO" />
<item index="2" class="java.lang.String" itemvalue="SERVER PROBLEM" />
<item index="3" class="java.lang.String" itemvalue="WEAK WARNING" />
<item index="4" class="java.lang.String" itemvalue="INFO" />
<item index="5" class="java.lang.String" itemvalue="WARNING" />
<item index="6" class="java.lang.String" itemvalue="ERROR" />
</list>
</settings>
</component>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EslintConfiguration">
<option name="fix-on-save" value="true" />
</component>
</project>

View file

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

View file

@ -1,17 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Tests" type="mocha-javascript-test-runner">
<node-interpreter>project</node-interpreter>
<node-options />
<mocha-package>yarn:package.json:mocha</mocha-package>
<working-directory>$PROJECT_DIR$</working-directory>
<pass-parent-env>true</pass-parent-env>
<envs>
<env name="NODE_CONFIG_ENV" value="tests" />
</envs>
<ui>bdd</ui>
<extra-mocha-options>-r ts-node/register -r tsconfig-paths/register</extra-mocha-options>
<test-kind>PATTERN</test-kind>
<test-pattern>tests/**/*.ts</test-pattern>
<method v="2" />
</configuration>
</component>

View file

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="clean" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="clean" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="commit" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="commit" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="compile:dev" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="compile:dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="lint" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="lint" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="run:dev" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="run:dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="watch:bot" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="watch:bot" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="watch:tsc" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="watch:tsc" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -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>

View file

@ -1,6 +0,0 @@
{
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint"
]
}

15
.vscode/settings.json vendored
View file

@ -1,15 +0,0 @@
{
"search.exclude": {
"**/.yarn": true,
},
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"typescript.enablePromptUseWorkspaceTsdk": true,
"eslint.format.enable": true,
"eslint.lintTask.enable": true,
"eslint.packageManager": "yarn",
"editor.insertSpaces": false,
"editor.tabSize": 2,
"typescript.preferences.quoteStyle": "single"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

768
.yarn/releases/yarn-3.1.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,7 @@
nmMode: hardlinks-local
nodeLinker: node-modules
plugins: plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript" spec: "@yarnpkg/plugin-typescript"
@ -6,6 +10,4 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools" spec: "@yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-sources.cjs yarnPath: .yarn/releases/yarn-3.1.0.cjs
nodeLinker: node-modules

View file

@ -1 +1 @@
# Argon Bot UwU

View file

@ -2,5 +2,12 @@
"token": "", "token": "",
"logLevel": "", "logLevel": "",
"prefix": "", "prefix": "",
"owner": "" "owner": "",
"db": {
"host": "",
"port": "0000",
"user": "",
"password": "",
"database": ""
}
} }

View file

@ -0,0 +1,31 @@
/*
* This file is part of ArgonBot
*
* ArgonBot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ArgonBot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>.
*/
exports.shorthands = undefined;
exports.up = (pgm) => {
pgm.createTable('settings', {
id: {
type: 'varchar(20)',
},
locale: {
type: 'varchar(10)',
default: 'en-US',
notNull: true,
},
});
};

View file

@ -21,6 +21,7 @@
"coverage": "NODE_CONFIG_ENV=tests nyc yarn run test", "coverage": "NODE_CONFIG_ENV=tests nyc yarn run test",
"lint": "eslint --format=pretty src/**/*.ts", "lint": "eslint --format=pretty src/**/*.ts",
"lint:save": "eslint --format=pretty --save src/**/*.ts", "lint:save": "eslint --format=pretty --save src/**/*.ts",
"migrate": "node-pg-migrate",
"postinstall": "husky install", "postinstall": "husky install",
"release:alpha": "standard-version --prelease alpha -s", "release:alpha": "standard-version --prelease alpha -s",
"release:beta": "standard-version --prelease beta -s", "release:beta": "standard-version --prelease beta -s",
@ -33,74 +34,86 @@
"watch:tsc": "tsc --watch" "watch:tsc": "tsc --watch"
}, },
"dependencies": { "dependencies": {
"@discordjs/opus": "^0.5.3", "@discordjs/opus": "^0.6.0",
"@discordjs/voice": "^0.5.1", "@discordjs/voice": "^0.7.2",
"bufferutil": "^4.0.3", "bufferutil": "^4.0.5",
"chalk": "^4.1.1", "chalk": "^4.1.2",
"config": "^3.3.6", "config": "^3.3.6",
"discord.js": "dev", "discord.js": "^13.3.0",
"erlpack": "^0.1.3", "erlpack": "^0.1.3",
"ffmpeg-static": "^4.4.0", "ffmpeg-static": "^4.4.0",
"figlet": "^1.5.0", "figlet": "^1.5.2",
"gradient-string": "^1.2.0", "gradient-string": "^1.2.0",
"i18next": "^20.3.2", "i18next": "^21.3.3",
"i18next-fluent": "^1.0.1", "i18next-fluent": "^2.0.0",
"i18next-fs-backend": "^1.1.1", "i18next-fs-backend": "^1.1.1",
"jsonschema": "^1.4.0", "jsonschema": "^1.4.0",
"luxon": "^1.27.0", "luxon": "^2.0.2",
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
"node-gyp": "^8.1.0", "node-pg-migrate": "^6.0.0",
"pg": "^8.7.1",
"sodium": "^3.0.2", "sodium": "^3.0.2",
"sql-template-strings": "^2.2.2",
"terminal-link": "^3.0.0", "terminal-link": "^3.0.0",
"tslib": "^2.3.0", "tslib": "^2.3.1",
"typescript": "^4.3.4", "typescript": "^4.4.4",
"utf-8-validate": "^5.0.5", "utf-8-validate": "^5.0.7",
"vm2": "^3.9.3", "vm2": "^3.9.5",
"zlib-sync": "^0.1.7" "zlib-sync": "^0.1.7"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^12.1.4", "@commitlint/cli": "^13.2.1",
"@commitlint/config-conventional": "^12.1.4", "@commitlint/config-conventional": "^13.2.0",
"@commitlint/prompt-cli": "^12.1.4", "@commitlint/prompt-cli": "^13.2.1",
"@istanbuljs/nyc-config-typescript": "^1.0.1", "@istanbuljs/nyc-config-typescript": "^1.0.1",
"@rollup/plugin-json": "^4.1.0", "@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-typescript": "^8.2.1", "@rollup/plugin-strip": "^2.1.0",
"@types/chai": "^4.2.19", "@rollup/plugin-typescript": "^8.3.0",
"@types/config": "^0.0.38", "@types/chai": "^4.2.22",
"@types/eslint": "^7.2.13", "@types/config": "^0.0.40",
"@types/figlet": "^1.5.1", "@types/eslint": "^7.28.2",
"@types/gradient-string": "^1.1.1", "@types/figlet": "^1.5.4",
"@types/i18next-fs-backend": "^1.0.0", "@types/gradient-string": "^1.1.2",
"@types/luxon": "^1.27.0", "@types/i18next-fs-backend": "^1.1.2",
"@types/mocha": "^8.2.2", "@types/luxon": "^2.0.5",
"@types/module-alias": "^2.0.0", "@types/mocha": "^8.2.3",
"@types/node": "^15.12.5", "@types/module-alias": "^2.0.1",
"@types/rimraf": "^3.0.0", "@types/node": "^16.11.6",
"@types/sinon": "^10.0.2", "@types/pg": "^8.6.1",
"@types/source-map-support": "^0.5.3", "@types/rimraf": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.28.1", "@types/rollup-plugin-progress": "^1.1.1",
"@typescript-eslint/parser": "^4.28.1", "@types/rollup-plugin-size-snapshot": "^0.10.1",
"@typescript-eslint/typescript-estree": "^4.28.1", "@types/sinon": "^10.0.6",
"@types/source-map-support": "^0.5.4",
"@typescript-eslint/eslint-plugin": "^5.2.0",
"@typescript-eslint/parser": "^5.2.0",
"@typescript-eslint/typescript-estree": "^5.2.0",
"chai": "^4.3.4", "chai": "^4.3.4",
"eslint": "^7.29.0", "eslint": "^8.1.0",
"eslint-formatter-pretty": "^4.1.0", "eslint-formatter-pretty": "^4.1.0",
"eslint-plugin-header": "^3.1.1", "eslint-plugin-header": "^3.1.1",
"husky": "^6.0.0", "husky": "^7.0.4",
"i18next-chained-backend": "^3.0.2", "i18next-chained-backend": "^3.0.2",
"lint-staged": "^11.0.0", "lint-staged": "^11.2.6",
"mocha": "^9.0.1", "mocha": "^9.1.3",
"nodemon": "^2.0.8", "nodemon": "^2.0.14",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^2.52.3", "rollup": "^2.58.3",
"rollup-plugin-analyzer": "^4.0.0",
"rollup-plugin-multi-input": "^1.3.1", "rollup-plugin-multi-input": "^1.3.1",
"rollup-plugin-progress": "^1.1.2",
"rollup-plugin-size-snapshot": "^0.12.0",
"rollup-plugin-sizes": "^1.0.4",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"sinon": "^11.1.1", "rollup-plugin-visualizer": "^5.5.2",
"source-map-support": "^0.5.19", "sinon": "^11.1.2",
"standard-version": "^9.3.0", "source-map-support": "^0.5.20",
"ts-node": "^10.0.0", "standard-version": "^9.3.2",
"tsconfig-paths": "^3.9.0", "ts-node": "^10.4.0",
"typescript-eslint-language-service": "^4.1.4" "ts-sql-plugin": "^0.10.0",
"tsconfig-paths": "^3.11.0",
"typescript-eslint-language-service": "^4.1.5"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,
@ -164,29 +177,6 @@
"license-header.txt" "license-header.txt"
], ],
"no-case-declarations": "off", "no-case-declarations": "off",
"keyword-spacing": [
"error",
{
"overrides": {
"if": {
"before": false,
"after": false
},
"catch": {
"before": true,
"after": false
},
"switch": {
"before": true,
"after": false
},
"for": {
"before": true,
"after": false
}
}
}
],
"@typescript-eslint/no-non-null-assertion": "off" "@typescript-eslint/no-non-null-assertion": "off"
}, },
"reportUnusedDisableDirectives": true "reportUnusedDisableDirectives": true

View file

@ -18,6 +18,8 @@ import typescript from '@rollup/plugin-typescript';
import json from '@rollup/plugin-json'; import json from '@rollup/plugin-json';
import { terser } from 'rollup-plugin-terser'; import { terser } from 'rollup-plugin-terser';
import multiInput from 'rollup-plugin-multi-input'; import multiInput from 'rollup-plugin-multi-input';
import progress from 'rollup-plugin-progress';
import analyze from 'rollup-plugin-analyzer';
export default { export default {
input: ['src/index.ts', 'src/commands/**/*.ts'], input: ['src/index.ts', 'src/commands/**/*.ts'],
@ -30,5 +32,7 @@ export default {
json({ compact: true, preferConst: true }), json({ compact: true, preferConst: true }),
terser(), terser(),
multiInput(), multiInput(),
progress(),
analyze(),
], ],
}; };

View file

@ -0,0 +1,102 @@
/*
* This file is part of ArgonBot
*
* ArgonBot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ArgonBot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>.
*/
import Command from '@structures/command';
import { ECommandRunIn, IGuildSettings } from '@utils/types';
import { query } from '@utils/utils';
import type { Client, Message } from 'discord.js';
import i18next from 'i18next';
import SQL from 'sql-template-strings';
export default class extends Command {
public constructor(client: Client, file: string) {
super(client, file, {
shortDescription: i18next.t('commands:settings.shortDescription'),
extendedDescription: i18next.t('commands:settings.extendedDescription'),
usage: 'set locale en-US',
runIn: ECommandRunIn.Server,
args: [
{
name: 'action',
acceptedValues: [ 'set', 'get', 'reset' ],
description: i18next.t('commands:settings.actionArg'),
},
{
name: 'setting',
description: i18next.t('commands:settings.settingArg'),
},
{
name: 'value',
description: i18next.t('commands:settings.valueArg'),
optional: true,
goToEnd: true,
},
],
});
}
public async run(message: Message, action: string, setting: string, value: string): Promise<void> {
switch(action) {
case 'set':
await this.setSetting(message, setting, value);
break;
case 'get':
break;
case 'reset':
break;
default:
break;
}
}
private async setSetting(message: Message, setting: string, value: string): Promise<void> {
let findGuild = await query<IGuildSettings>(SQL`SELECT *
FROM bot.public.settings
WHERE id = ${message.guild?.id}`);
if(findGuild && findGuild.rowCount === 0) {
await query(SQL`INSERT INTO bot.public.settings (id)
VALUES (${message.guild?.id})`);
findGuild = await query<IGuildSettings>(SQL`SELECT *
FROM bot.public.settings
WHERE id = ${message.guild?.id}`);
}
switch(setting) {
case 'locale':
case 'language':
if(findGuild) {
const guildSettings = findGuild.rows[0]!;
if(guildSettings.locale === value)
await message.reply(i18next.t('commands:settings.localeAlreadySet', { locale: guildSettings.locale }));
else {
await query(SQL`UPDATE bot.public.settings
SET locale=${value}
WHERE id = ${message.guild?.id}`);
await message.channel.send(i18next.t('commands:settings.localeSuccessfullySet', { locale: value }));
}
}
break;
case 'get':
break;
case 'reset':
break;
default:
break;
}
}
}

View file

@ -15,17 +15,17 @@
* along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>. * along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>.
*/ */
import Command from '@structures/command'; import Command from '@structures/command';
import { capitalize } from '@utils/utils';
import config from 'config'; import config from 'config';
import { Client, Message, MessageButton, MessageEmbed, MessageSelectMenu, MessageSelectOptionData } from 'discord.js'; import { Client, Message, MessageButton, MessageEmbed, MessageSelectMenu, MessageSelectOptionData } from 'discord.js';
import i18next from 'i18next'; import i18next from 'i18next';
// TODO: Redo this mf
export default class extends Command { export default class extends Command {
public constructor(client: Client, file: string) { public constructor(client: Client, file: string) {
super(client, file, { super(client, file, {
shortDescription: i18next.t('commands:help.shortDescription'), shortDescription: i18next.t('commands:help.shortDescription'),
extendedDescription: i18next.t('commands:help.extendedDescription'), extendedDescription: i18next.t('commands:help.extendedDescription'),
usage: 'vm',
args: [ args: [
{ {
name: 'command', name: 'command',
@ -50,7 +50,7 @@ export default class extends Command {
commandGroups.push({ commandGroups.push({
name: command.options.group!, name: command.options.group!,
embed: new MessageEmbed(), embed: new MessageEmbed(),
commands: [command], commands: [ command ],
}); });
categories.push(command.options.group!); categories.push(command.options.group!);
@ -60,7 +60,7 @@ export default class extends Command {
commandGroups.forEach((val, index) => { commandGroups.forEach((val, index) => {
val.embed = new MessageEmbed(); val.embed = new MessageEmbed();
val.embed.setAuthor(i18next.t('commands:help.embedName')); val.embed.setAuthor(i18next.t('commands:help.embedName'));
val.embed.setTitle(val.name.toUpperCase()); val.embed.setTitle(capitalize(val.name));
val.embed.setColor(message.member?.roles.highest.color ?? 0xFFFFFF); val.embed.setColor(message.member?.roles.highest.color ?? 0xFFFFFF);
val.embed.setFooter(`Page ${index + 1}/${commandGroups.length}`); val.embed.setFooter(`Page ${index + 1}/${commandGroups.length}`);
val.embed.setTimestamp(); val.embed.setTimestamp();
@ -89,15 +89,15 @@ export default class extends Command {
chooseHelpCategory.setPlaceholder(i18next.t('commands:help.categoryPlaceholder')); chooseHelpCategory.setPlaceholder(i18next.t('commands:help.categoryPlaceholder'));
for(const category of categories) { for(const category of categories) {
dropDownOptions.push({ label: category, value: category }); dropDownOptions.push({ label: capitalize(category), value: category });
} }
chooseHelpCategory.addOptions(dropDownOptions); chooseHelpCategory.addOptions(dropDownOptions);
const helpMsg = await message.channel.send({ const helpMsg = await message.channel.send({
content: i18next.t('commands:help.helpScreenBtnHelp'), content: i18next.t('commands:help.helpScreenBtnHelp'),
embeds: [commandGroups[0]!.embed], embeds: [ commandGroups[0]!.embed ],
components: [[previousCategoryBtn, nextCategoryBtn], [chooseHelpCategory]], components: [ [ previousCategoryBtn, nextCategoryBtn ], [ chooseHelpCategory ] ],
}); });
const buttonCollector = helpMsg.channel.createMessageComponentInteractionCollector({ time: 60000 }); const buttonCollector = helpMsg.channel.createMessageComponentInteractionCollector({ time: 60000 });
@ -111,33 +111,33 @@ export default class extends Command {
previousCategoryBtn.setDisabled(false); previousCategoryBtn.setDisabled(false);
} }
if(currentPage !== 0) { if(currentPage + 1 !== commandGroups.length) {
nextCategoryBtn.setDisabled(false); nextCategoryBtn.setDisabled(false);
previousCategoryBtn.setDisabled(true); previousCategoryBtn.setDisabled(false);
} }
await interaction.update({ await interaction.update({
content: i18next.t('commands:help.helpScreenBtnHelp'), content: i18next.t('commands:help.helpScreenBtnHelp'),
embeds: [commandGroups[currentPage]!.embed], embeds: [ commandGroups[currentPage]!.embed ],
components: [[previousCategoryBtn, nextCategoryBtn], [chooseHelpCategory]], components: [ [ previousCategoryBtn, nextCategoryBtn ], [ chooseHelpCategory ] ],
}); });
} else if(interaction.customID === 'previousCategoryBtn') { } else if(interaction.customID === 'previousCategoryBtn') {
currentPage--; currentPage--;
if(currentPage + 1 !== commandGroups.length) {
nextCategoryBtn.setDisabled(false);
previousCategoryBtn.setDisabled(false);
}
if(currentPage === 0) { if(currentPage === 0) {
previousCategoryBtn.setDisabled(true); previousCategoryBtn.setDisabled(true);
nextCategoryBtn.setDisabled(false); nextCategoryBtn.setDisabled(false);
} }
if(currentPage !== 0) {
nextCategoryBtn.setDisabled(false);
previousCategoryBtn.setDisabled(true);
}
await interaction.update({ await interaction.update({
content: i18next.t('commands:help.helpScreenBtnHelp'), content: i18next.t('commands:help.helpScreenBtnHelp'),
embeds: [commandGroups[currentPage]!.embed], embeds: [ commandGroups[currentPage]!.embed ],
components: [[previousCategoryBtn, nextCategoryBtn], [chooseHelpCategory]], components: [ [ previousCategoryBtn, nextCategoryBtn ], [ chooseHelpCategory ] ],
}); });
} else if(interaction.customID === 'chooseHelpCategory') { } else if(interaction.customID === 'chooseHelpCategory') {
currentPage = categories.indexOf(interaction.values[0]); currentPage = categories.indexOf(interaction.values[0]);
@ -147,9 +147,9 @@ export default class extends Command {
nextCategoryBtn.setDisabled(false); nextCategoryBtn.setDisabled(false);
} }
if(currentPage !== 0) { if(currentPage + 1 !== commandGroups.length) {
nextCategoryBtn.setDisabled(false); nextCategoryBtn.setDisabled(false);
previousCategoryBtn.setDisabled(true); previousCategoryBtn.setDisabled(false);
} }
if(currentPage + 1 === commandGroups.length) { if(currentPage + 1 === commandGroups.length) {
@ -159,8 +159,8 @@ export default class extends Command {
await interaction.update({ await interaction.update({
content: i18next.t('commands:help.helpScreenBtnHelp'), content: i18next.t('commands:help.helpScreenBtnHelp'),
embeds: [commandGroups[currentPage]!.embed], embeds: [ commandGroups[currentPage]!.embed ],
components: [[previousCategoryBtn, nextCategoryBtn], [chooseHelpCategory]], components: [ [ previousCategoryBtn, nextCategoryBtn ], [ chooseHelpCategory ] ],
}); });
} }
}); });
@ -171,7 +171,8 @@ export default class extends Command {
await helpMsg.edit({ await helpMsg.edit({
content: i18next.t('commands:help.helpTimedOut'), content: i18next.t('commands:help.helpTimedOut'),
embeds: [commandGroups[currentPage]!.embed], embeds: [ commandGroups[currentPage]!.embed ],
components: [],
}); });
}); });
} else { } else {
@ -180,17 +181,18 @@ export default class extends Command {
if(findCommand) { if(findCommand) {
const commandEmbed = new MessageEmbed(); const commandEmbed = new MessageEmbed();
commandEmbed.setAuthor(i18next.t('commands:help.embedName')); commandEmbed.setAuthor(i18next.t('commands:help.embedName'));
commandEmbed.setTitle(findCommand.options.name!.toUpperCase()); commandEmbed.setTitle(capitalize(findCommand.options.name!));
commandEmbed.setColor(message.member?.roles.highest.color ?? 0xFFFFFF); commandEmbed.setColor(message.member?.roles.highest.color ?? 0xFFFFFF);
commandEmbed.setTimestamp(); commandEmbed.setTimestamp();
commandEmbed.setDescription(i18next.t('commands:help.commandDescription', { commandEmbed.setDescription(i18next.t('commands:help.commandDescription', {
name: findCommand.options.name, name: findCommand.options.name,
category: capitalize(findCommand.options.group!),
description: findCommand.options.extendedDescription ?? i18next.t('commands:generic.noExtendedDescription'), description: findCommand.options.extendedDescription ?? i18next.t('commands:generic.noExtendedDescription'),
usage: findCommand.options.usage ? `!${findCommand.options.name} ${findCommand.options.usage}` : i18next.t('commands:generic.noUsage'), usage: findCommand.options.usage ? `!${findCommand.options.name} ${findCommand.options.usage}` : i18next.t('commands:generic.noUsage'),
})); }));
await message.channel.send({ embeds: [commandEmbed] }); await message.channel.send({ embeds: [ commandEmbed ] });
} else { } else {
await message.reply(i18next.t('commands:help.unknownCommand')); await message.reply(i18next.t('commands:help.unknownCommand'));
} }

View file

@ -15,7 +15,6 @@
* along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>. * along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>.
*/ */
import Command from '@structures/command'; import Command from '@structures/command';
import type { Client, Message } from 'discord.js'; import type { Client, Message } from 'discord.js';
import i18next from 'i18next'; import i18next from 'i18next';
import { transpile } from 'typescript'; import { transpile } from 'typescript';
@ -26,10 +25,11 @@ export default class extends Command {
super(client, file, { super(client, file, {
shortDescription: i18next.t('commands:vm.shortDescription'), shortDescription: i18next.t('commands:vm.shortDescription'),
extendedDescription: i18next.t('commands:vm.extendedDescription'), extendedDescription: i18next.t('commands:vm.extendedDescription'),
usage: 'js const test = \'test\'; return test;',
args: [ args: [
{ {
name: 'language', name: 'language',
acceptedValues: ['js', 'javascript', 'ts', 'typescript'], acceptedValues: [ 'js', 'javascript', 'ts', 'typescript' ],
description: i18next.t('commands:vm.languageArg'), description: i18next.t('commands:vm.languageArg'),
}, },
{ {
@ -48,7 +48,7 @@ export default class extends Command {
switch(vmLang.toLowerCase()) { switch(vmLang.toLowerCase()) {
case 'js': case 'js':
case 'javascript': case 'javascript':
await message.reply(i18next.t('commands:vm.computing', { language: 'JavaScript' })); await message.channel.send(i18next.t('commands:vm.computing', { language: 'JavaScript' }));
try { try {
let returnVal: string; let returnVal: string;
@ -57,14 +57,14 @@ export default class extends Command {
else else
returnVal = await javascriptVM.run(`(() => {${code}})()`); returnVal = await javascriptVM.run(`(() => {${code}})()`);
await message.reply(`\`\`\`js\n${returnVal}\`\`\``); await message.channel.send(`\`\`\`js\n${returnVal}\`\`\``);
} catch(err) { } catch(err) {
await message.reply(i18next.t('commands:vm.error', { error: err.message, language: 'JavaScript' })); await message.reply(i18next.t('commands:vm.error', { error: err.message, language: 'JavaScript' }));
} }
break; break;
case 'ts': case 'ts':
case 'typescript': case 'typescript':
await message.reply(i18next.t('commands:vm.computing', { language: 'TypeScript' })); await message.channel.send(i18next.t('commands:vm.computing', { language: 'TypeScript' }));
try { try {
const jsCode = transpile(code); const jsCode = transpile(code);
@ -75,7 +75,7 @@ export default class extends Command {
else else
returnVal = await javascriptVM.run(`(() => {${jsCode}}()`); returnVal = await javascriptVM.run(`(() => {${jsCode}}()`);
await message.reply(`\`\`\`js\n${returnVal}\`\`\``); await message.channel.send(`\`\`\`js\n${returnVal}\`\`\``);
} catch(err) { } catch(err) {
await message.reply(i18next.t('commands:vm.error', { error: err.message, language: 'TypeScript' })); await message.reply(i18next.t('commands:vm.error', { error: err.message, language: 'TypeScript' }));
} }

View file

@ -21,9 +21,10 @@ import ArgonClient from '@lib/ArgonClient';
import { Defaults } from '@utils/defaults'; import { Defaults } from '@utils/defaults';
import { debug, error, fatal, info, verbose } from '@utils/logger'; import { debug, error, fatal, info, verbose } from '@utils/logger';
import { ECommandRunIn, ELoggingScope } from '@utils/types'; import { ECommandRunIn, ELoggingScope } from '@utils/types';
import { walkDir } from '@utils/utils'; import { connectToDB, walkDir } from '@utils/utils';
import chalk from 'chalk'; import chalk from 'chalk';
import config from 'config'; import config from 'config';
import { Collection } from 'discord.js';
import figlet from 'figlet'; import figlet from 'figlet';
import gradient from 'gradient-string'; import gradient from 'gradient-string';
import i18next from 'i18next'; import i18next from 'i18next';
@ -36,6 +37,7 @@ import { Validator } from 'jsonschema';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import process from 'process'; import process from 'process';
const commandCooldowns: Collection<string, Collection<string, number>> = new Collection();
let isBotReady = false; let isBotReady = false;
info('Starting bot... Please wait!', ELoggingScope.Startup); info('Starting bot... Please wait!', ELoggingScope.Startup);
@ -69,7 +71,8 @@ figlet('Argon Bot', (err, data) => {
}); });
const client = new ArgonClient({ const client = new ArgonClient({
intents: ['GUILDS', 'GUILD_MESSAGES'], intents: [ 'GUILDS', 'GUILD_MESSAGES', 'DIRECT_MESSAGES' ],
partials: [ 'CHANNEL' ],
}); });
client.on('message', async (msg) => { client.on('message', async (msg) => {
@ -89,26 +92,56 @@ client.on('message', async (msg) => {
if(findCommand.options.ownerOnly && msg.author.id !== config.get('owner')) { if(findCommand.options.ownerOnly && msg.author.id !== config.get('owner')) {
await msg.reply(i18next.t('commands:errors.ownerOnly')); await msg.reply(i18next.t('commands:errors.ownerOnly'));
msg.channel.stopTyping();
return; return;
} }
if((findCommand.options.runIn === ECommandRunIn.DM) && msg.channel.type !== 'dm') { if((findCommand.options.runIn === ECommandRunIn.DM) && msg.channel.type !== 'dm') {
await msg.reply(i18next.t('commands:errors.dmsOnly')); await msg.reply(i18next.t('commands:errors.dmsOnly'));
msg.channel.stopTyping();
return; return;
} }
if((findCommand.options.runIn === ECommandRunIn.Server) && msg.channel.type !== 'text') { if((findCommand.options.runIn === ECommandRunIn.Server) && msg.channel.type !== 'text') {
await msg.reply(i18next.t('commands:errors.serverOnly')); await msg.reply(i18next.t('commands:errors.serverOnly'));
msg.channel.stopTyping();
return; return;
} }
if(msg.author.id !== config.get('owner')) {
if(!commandCooldowns.has(findCommand.options.name!)) {
commandCooldowns.set(findCommand.options.name!, new Collection());
}
const now = Date.now();
const timestamps = commandCooldowns.get(findCommand.options.name!);
const cooldownAmount = findCommand.options.cooldown! * 1000;
if(timestamps!.has(msg.author.id)) {
const expirationTime = timestamps!.get(msg.author.id)! + cooldownAmount;
if(now < expirationTime) {
const timeLeft = (expirationTime - now) / 1000;
await msg.reply(i18next.t('commands:errors.cooldownError', {
time: timeLeft.toFixed(1),
command: findCommand.options.name,
}));
msg.channel.stopTyping();
return;
}
}
timestamps!.set(msg.author.id, now);
setTimeout(() => timestamps!.delete(msg.author.id), cooldownAmount);
}
try { try {
info(`Command ${findCommand.options.name} is being ran in ${msg.guild ? msg.guild.name : 'DMs'} by ${msg.author.username}`, ELoggingScope.Command); info(`Command ${findCommand.options.name} is being ran in ${msg.guild ? msg.guild.name : 'DMs'} by ${msg.author.username}`, ELoggingScope.Command);
await findCommand.run(msg, ...args); await findCommand.run(msg, ...args);
info(`Finished running ${findCommand.options.name} in ${msg.guild ? msg.guild.name : `${msg.author.username} DMs`}`, ELoggingScope.Command); info(`Finished running ${findCommand.options.name} in ${msg.guild ? msg.guild.name : `${msg.author.username} DMs`}`, ELoggingScope.Command);
msg.channel.stopTyping(); msg.channel.stopTyping();
} catch(e) { } catch(e) {
console.error(e);
error(`An error has occurred while running ${findCommand.options.name}!\n${e.message}`, ELoggingScope.Command); error(`An error has occurred while running ${findCommand.options.name}!\n${e.message}`, ELoggingScope.Command);
await msg.reply(i18next.t('commands:errors.runError', { error: e.message })); await msg.reply(i18next.t('commands:errors.runError', { error: e.message }));
msg.channel.stopTyping(); msg.channel.stopTyping();
@ -119,14 +152,14 @@ client.on('ready', async () => {
info('Loading translations...', ELoggingScope.Startup); info('Loading translations...', ELoggingScope.Startup);
await i18next.use(Fluent).use(Backend).init({ await i18next.use(Fluent).use(Backend).init({
supportedLngs: ['en-US', 'owo'], supportedLngs: [ 'en-US', 'owo' ],
lng: 'en-US', lng: 'en-US',
fallbackLng: 'en-US', fallbackLng: 'en-US',
defaultNS: 'common', defaultNS: 'common',
fallbackNS: 'common', fallbackNS: 'common',
ns: ['common', 'commands'], ns: [ 'common', 'commands' ],
backend: { backend: {
backends: [FSBackend], backends: [ FSBackend ],
backendOptions: [ backendOptions: [
{ {
loadPath: `${process.cwd()}/translations/{{lng}}/{{ns}}.json`, loadPath: `${process.cwd()}/translations/{{lng}}/{{ns}}.json`,
@ -137,7 +170,7 @@ client.on('ready', async () => {
info('Finished loading translations.', ELoggingScope.Startup); info('Finished loading translations.', ELoggingScope.Startup);
info('Loading commands...', ELoggingScope.Startup); info('Loading commands...', ELoggingScope.Command);
try { try {
const files = await walkDir(`${__dirname}/commands`); const files = await walkDir(`${__dirname}/commands`);
@ -151,16 +184,20 @@ client.on('ready', async () => {
client.commands.set(command.options.name, command); client.commands.set(command.options.name, command);
debug(`Loaded command ${command.options.name}`, ELoggingScope.Startup); debug(`Loaded command ${command.options.name}`, ELoggingScope.Command);
} }
}); });
info(`Finished loading commands! Found ${client.commands.size} commands.`, ELoggingScope.Startup); info(`Finished loading commands! Found ${client.commands.size} commands.`, ELoggingScope.Command);
} catch(err) { } catch(err) {
console.log(err); console.log(err);
fatal(`An error has occurred while attempting to load command files! Please see error below\n${err.message}`, ELoggingScope.Startup); fatal(`An error has occurred while attempting to load command files! Please see error below\n${err.message}`, ELoggingScope.Command);
} }
info('Creating Database Pool...', ELoggingScope.Database);
await connectToDB(config.get('db'));
info('Finished creating Database Pool', ELoggingScope.Database);
info('Bot is ready!', ELoggingScope.Startup); info('Bot is ready!', ELoggingScope.Startup);
debug(`Total number of Servers: ${client.guilds.cache.size}`, ELoggingScope.Startup); debug(`Total number of Servers: ${client.guilds.cache.size}`, ELoggingScope.Startup);
debug(`Total number of Users: ${client.users.cache.size}`, ELoggingScope.Startup); debug(`Total number of Users: ${client.users.cache.size}`, ELoggingScope.Startup);

View file

@ -18,9 +18,11 @@ import type Command from '@structures/command';
import { Client, ClientOptions, Collection } from 'discord.js'; import { Client, ClientOptions, Collection } from 'discord.js';
export default class extends Client { export default class extends Client {
public override readonly commands: Collection<string, Command> = new Collection(); public commands: Collection<string, Command>;
public constructor(options: ClientOptions) { public constructor(options: ClientOptions) {
super(options); super(options);
this.commands = new Collection<string, Command>();
} }
} }

View file

@ -0,0 +1,16 @@
/*
* This file is part of ArgonBot
*
* ArgonBot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ArgonBot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>.
*/

View file

@ -31,6 +31,7 @@ export default abstract class Command {
const defaultOptions: ICommandOptions = { const defaultOptions: ICommandOptions = {
name: path.basename(this.file, path.extname(this.file)), name: path.basename(this.file, path.extname(this.file)),
group: path.basename(path.dirname(this.file)) === 'commands' ? '' : path.basename(path.dirname(this.file)), group: path.basename(path.dirname(this.file)) === 'commands' ? '' : path.basename(path.dirname(this.file)),
cooldown: 5,
}; };
this.options = config.util.extendDeep(defaultOptions, options); this.options = config.util.extendDeep(defaultOptions, options);

View file

@ -14,13 +14,10 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>. * along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>.
*/ */
import type ArgonClient from '@lib/ArgonClient';
import type Command from '@structures/command'; import type Command from '@structures/command';
declare module 'discord.js' { declare module 'discord.js' {
export interface Client { interface Client{
constructor: typeof ArgonClient; commands: Collection<string, Command>;
readonly commands: Collection<string, Command>;
} }
} }

View file

@ -19,7 +19,7 @@ export const Defaults = {
logLevel: 'info', logLevel: 'info',
}, },
configSchema: { configSchema: {
required: ['token', 'logLevel', 'prefix', 'owner'], required: [ 'token', 'logLevel', 'prefix', 'owner', 'db' ],
type: 'object', type: 'object',
properties: { properties: {
token: { token: {
@ -46,6 +46,33 @@ export const Defaults = {
$id: '#/properties/owner', $id: '#/properties/owner',
type: 'string', type: 'string',
}, },
db: {
$id: '#/properties/db',
type: 'object',
required: [ 'host', 'port', 'user', 'password', 'database' ],
properties: {
host: {
$id: '#/properties/db/host',
type: 'string',
},
port: {
$id: '#/properties/db/port',
type: 'string',
},
user: {
$id: '#/properties/db/user',
type: 'string',
},
password: {
$id: '#/properties/db/password',
type: 'string',
},
database: {
$id: '#/properties/db/database',
type: 'string',
},
},
},
}, },
additionalProperties: false, additionalProperties: false,
}, },

View file

@ -21,6 +21,7 @@ export enum ELoggingScope {
Command = 'COMMAND', Command = 'COMMAND',
Payload = 'PAYLOAD', Payload = 'PAYLOAD',
Query = 'QUERY', Query = 'QUERY',
Database = 'DATABASE',
} }
export enum ECommandRunIn { export enum ECommandRunIn {
@ -37,6 +38,7 @@ export interface ICommandOptions {
extendedDescription?: string; extendedDescription?: string;
usage?: string; usage?: string;
ownerOnly?: boolean; ownerOnly?: boolean;
cooldown?: number;
runIn?: ECommandRunIn; runIn?: ECommandRunIn;
args?: { args?: {
name: string; name: string;
@ -47,4 +49,9 @@ export interface ICommandOptions {
}[]; }[];
} }
export interface IGuildSettings {
id: string,
locale: string;
}
// Type Aliases // Type Aliases

View file

@ -14,8 +14,14 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>. * along with ArgonBot. If not, see <https: //www.gnu.org/licenses/>.
*/ */
import { error, info } from '@utils/logger';
import { ELoggingScope } from '@utils/types';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import pg, { QueryResult } from 'pg';
import type { SQLStatement } from 'sql-template-strings';
let pool: pg.Pool;
export const walkDir = async (dir: string): Promise<string[]> => { export const walkDir = async (dir: string): Promise<string[]> => {
let results: string[] = []; let results: string[] = [];
@ -53,3 +59,48 @@ export const walkDir = async (dir: string): Promise<string[]> => {
}); });
}); });
}; };
export const capitalize = (string: string): string => {
return string.charAt(0).toUpperCase() + string.slice(1);
};
export const connectToDB = async (connectionData: pg.PoolConfig): Promise<void> => {
pool = new pg.Pool(connectionData);
pool.on('connect', () => {
info('Connected to the Postgres Database!', ELoggingScope.Database);
});
pool.on('error', (err) => {
error(`An error has occurred with node-postgres!\n${err.message}`, ELoggingScope.Database);
});
};
export const query = async <t>(query: SQLStatement): Promise<QueryResult<t> | void> => {
if(!pool) return error('Attempted to query to the Database when a connection has not been made yet... Discarding' +
' request.', ELoggingScope.Query);
else {
const client = await pool.connect();
try {
info('BEGIN', ELoggingScope.Query);
await client.query('BEGIN');
info(query.text, ELoggingScope.Query);
const result = await client.query<t>(query);
info('COMMIT', ELoggingScope.Query);
await client.query('COMMIT');
return result;
} catch(e) {
error(`An error has occurred while attempting to query "${query}"... Rolling back changes`, ELoggingScope.Database);
info('ROLLBACK', ELoggingScope.Query);
await client.query('ROLLBACK');
throw e;
} finally {
client.release();
}
}
};

View file

@ -30,7 +30,7 @@ describe('verbose()', () => {
verbose('Test Message', ELoggingScope.Command); verbose('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch('Test Message')).to.be.true; expect(spy.calledWithMatch('Test Message')).to.be.true;
}); });
it('check if Logging Scopes are being logged', () => { it('check if Logging Scopes are being logged', () => {
verbose('Test Message', ELoggingScope.Command); verbose('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch(/COMMAND/)).to.be.true; expect(spy.calledWithMatch(/COMMAND/)).to.be.true;
@ -51,7 +51,7 @@ describe('debug()', () => {
debug('Test Message', ELoggingScope.Command); debug('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch('Test Message')).to.be.true; expect(spy.calledWithMatch('Test Message')).to.be.true;
}); });
it('check if Logging Scopes are being logged', () => { it('check if Logging Scopes are being logged', () => {
debug('Test Message', ELoggingScope.Command); debug('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch(/COMMAND/)).to.be.true; expect(spy.calledWithMatch(/COMMAND/)).to.be.true;
@ -72,7 +72,7 @@ describe('info()', () => {
info('Test Message', ELoggingScope.Command); info('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch('Test Message')).to.be.true; expect(spy.calledWithMatch('Test Message')).to.be.true;
}); });
it('check if Logging Scopes are being logged', () => { it('check if Logging Scopes are being logged', () => {
info('Test Message', ELoggingScope.Command); info('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch(/COMMAND/)).to.be.true; expect(spy.calledWithMatch(/COMMAND/)).to.be.true;
@ -93,7 +93,7 @@ describe('warn()', () => {
warn('Test Message', ELoggingScope.Command); warn('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch('Test Message')).to.be.true; expect(spy.calledWithMatch('Test Message')).to.be.true;
}); });
it('check if Logging Scopes are being logged', () => { it('check if Logging Scopes are being logged', () => {
warn('Test Message', ELoggingScope.Command); warn('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch(/COMMAND/)).to.be.true; expect(spy.calledWithMatch(/COMMAND/)).to.be.true;
@ -114,7 +114,7 @@ describe('error()', () => {
error('Test Message', ELoggingScope.Command); error('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch('Test Message')).to.be.true; expect(spy.calledWithMatch('Test Message')).to.be.true;
}); });
it('check if Logging Scopes are being logged', () => { it('check if Logging Scopes are being logged', () => {
error('Test Message', ELoggingScope.Command); error('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch(/COMMAND/)).to.be.true; expect(spy.calledWithMatch(/COMMAND/)).to.be.true;
@ -135,7 +135,7 @@ describe('fatal()', () => {
fatal('Test Message', ELoggingScope.Command); fatal('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch('Test Message')).to.be.true; expect(spy.calledWithMatch('Test Message')).to.be.true;
}); });
it('check if Logging Scopes are being logged', () => { it('check if Logging Scopes are being logged', () => {
fatal('Test Message', ELoggingScope.Command); fatal('Test Message', ELoggingScope.Command);
expect(spy.calledWithMatch(/COMMAND/)).to.be.true; expect(spy.calledWithMatch(/COMMAND/)).to.be.true;

View file

@ -18,14 +18,24 @@
"nextCategoryBtn": "Next Category", "nextCategoryBtn": "Next Category",
"previousCategoryBtn": "Previous Category", "previousCategoryBtn": "Previous Category",
"helpTimedOut": "Timed Out", "helpTimedOut": "Timed Out",
"commandDescription": "Name: { $name }\nCategory: { $category }\nDescription: { $description }\nUsage: { $usage }", "commandDescription": "Name: `{ $name }`\nCategory: `{ $category }`\nDescription: `{ $description }`\nUsage: `{ $usage }`",
"categoryPlaceholder": "Choose a category!" "categoryPlaceholder": "Choose a category!"
}, },
"settings": {
"shortDescription": "Changes the current server settings",
"extendedDescription": "Changes the current server settings for better server customization",
"actionArg": "What action you want to take",
"settingArg": "What setting you want to view/set\nOr optionally, you can put 'help' if you are unsure what to do with that action",
"valueArg": "The value you want to set (This is ignored if you are only getting/resetting a setting).",
"localeAlreadySet": "The language of the bot in this server is already set to { $locale }!",
"localeSuccessfullySet": "The language of the bot has been successfully set to { $locale }!"
},
"errors": { "errors": {
"ownerOnly": "Only the bot owner can run this command!", "ownerOnly": "Only the bot owner can run this command!",
"dmsOnly": "You can only run this command in DMs!", "dmsOnly": "You can only run this command in DMs!",
"serverOnly": "You can only run this command in a server!", "serverOnly": "You can only run this command in a server!",
"runError": "I'm sorry but an error has occurred while running this command! Please file an issue to https://code.relms.dev/Relms/ArgonBot!\n```{ $error }```" "runError": "I'm sorry but an error has occurred while running this command! Please file an issue to https://code.relms.dev/Relms/ArgonBot!\n```{ $error }```",
"cooldownError": "Please wait { $time } more second(s) before reusing the `{ $command }` command."
}, },
"generic": { "generic": {
"noShortDescription": "No short description given!", "noShortDescription": "No short description given!",

View file

@ -18,14 +18,15 @@
"nextCategoryBtn": "Nyext Categowy", "nextCategoryBtn": "Nyext Categowy",
"previousCategoryBtn": "Pwevious Categowy", "previousCategoryBtn": "Pwevious Categowy",
"helpTimedOut": "Timed Out", "helpTimedOut": "Timed Out",
"commandDescription": "Nyame: { $name }\nCategowy: { $category }\nDescwiption: { $description }\nUsage: { $usage }", "commandDescription": "Nyame: `{ $name }`\nCategowy: `{ $category }`\nDescwiption: `{ $description }`\nUsage: `{ $usage }`",
"categoryPlaceholder": "Choose a categowy (・`ω´・)" "categoryPlaceholder": "Choose a categowy (・`ω´・)"
}, },
"errors": { "errors": {
"ownerOnly": "Onwy the bot ownyew can wun this command ^w^", "ownerOnly": "Onwy the bot ownyew can wun this command ^w^",
"dmsOnly": "You can onwy wun this command in DMs owo", "dmsOnly": "You can onwy wun this command in DMs owo",
"serverOnly": "You can onwy wun this command in a sewvew owo", "serverOnly": "You can onwy wun this command in a sewvew owo",
"runError": "I'm sowwy but an ewwow has occuwwed whiwe wunnying this command ;;w;; Pwease fiwe an issue to https://code.relms.dev/Relms/ArgonBot!\n```{ $error }```" "runError": "I'm sowwy but an ewwow has occuwwed whiwe wunnying this command ;;w;; Pwease fiwe an issue to https://code.relms.dev/Relms/ArgonBot!\n```{ $error }```",
"cooldownError": "Pwease wait { $time } mowe second(s) befowe weusing the `{ $command }` command."
}, },
"generic": { "generic": {
"noShortDescription": "Nyo showt descwiption given >w<", "noShortDescription": "Nyo showt descwiption given >w<",

View file

@ -25,9 +25,7 @@
"strictPropertyInitialization": true, "strictPropertyInitialization": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"esModuleInterop": true, "esModuleInterop": true,
"moduleResolution": "Node",
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true, "noUncheckedIndexedAccess": true,
@ -52,9 +50,19 @@
"inlineSourceMap": true, "inlineSourceMap": true,
"inlineSources": true, "inlineSources": true,
"baseUrl": ".", "baseUrl": ".",
"moduleResolution": "Node",
"plugins": [ "plugins": [
{ {
"name": "typescript-eslint-language-service" "name": "typescript-eslint-language-service"
},
{
"name": "ts-sql-plugin",
"mock": "0",
"cost_pattern": "/\\(cost=\\d+\\.?\\d*\\.\\.(\\d+\\.?\\d*)/",
"error_cost": null,
"warn_cost": null,
"info_cost": null,
"schema_command": "pg"
} }
], ],
"paths": { "paths": {

5116
yarn.lock

File diff suppressed because it is too large Load diff