Compare commits

..

No commits in common. "master" and "old" have entirely different histories.
master ... old

18 changed files with 940 additions and 59 deletions

View File

@ -1,3 +0,0 @@
.env
.venv/
__pycache__/

View File

@ -1 +0,0 @@
DISCORD_TOKEN=

7
.gitignore vendored
View File

@ -1,3 +1,4 @@
.venv/
.env
__pycache__/
.idea/
node_modules/
dist/
src/config.json

View File

@ -1,10 +0,0 @@
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

View File

@ -1,6 +0,0 @@
services:
conjure_bot:
build: .
restart: unless-stopped
environment:
- DISCORD_TOKEN=${DISCORD_TOKEN}

34
main.py
View File

@ -1,34 +0,0 @@
import os
import discord
from discord import app_commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
def main():
class MyBot(discord.Client):
def __init__(self):
intents = discord.Intents.default()
super().__init__(intents=intents)
self.tree = app_commands.CommandTree(self)
async def setup_hook(self):
# This is the "clean" way for production bots
await self.tree.sync()
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
bot = MyBot()
@bot.tree.command(name="ping", description="Check the bot's speed")
async def ping(interaction: discord.Interaction):
# Modern bots use interaction.response instead of ctx.send
await interaction.response.send_message(f'Pong! Latency: {round(bot.latency * 1000)} ms')
bot.run(TOKEN)
if __name__ == '__main__':
main()

443
package-lock.json generated Normal file
View File

@ -0,0 +1,443 @@
{
"name": "conjurebot",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@discordjs/collection": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.5.tgz",
"integrity": "sha512-CU1q0UXQUpFNzNB7gufgoisDHP7n+T3tkqTsp3MNUkVJ5+hS3BCvME8uCXAUFlz+6T2FbTCu75A+yQ7HMKqRKw=="
},
"@sindresorhus/is": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.0.tgz",
"integrity": "sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg=="
},
"@szmarczak/http-timer": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
"requires": {
"defer-to-connect": "^2.0.0"
}
},
"@types/cacheable-request": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
"integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
"requires": {
"@types/http-cache-semantics": "*",
"@types/keyv": "*",
"@types/node": "*",
"@types/responselike": "*"
}
},
"@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
},
"@types/keyv": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
"integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
"requires": {
"@types/node": "*"
}
},
"@types/mysql": {
"version": "2.15.9",
"resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.9.tgz",
"integrity": "sha512-rB3w3/YEV11oIoL56iP4OPt6uLkcuu6oIqbUy8T2bSm/ZUYN0fvyyzzrZBDNYL//zRStdmSsUPZDtHXjdR1hTA==",
"requires": {
"@types/node": "*"
}
},
"@types/node": {
"version": "13.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.0.tgz",
"integrity": "sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ=="
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
"requires": {
"@types/node": "*"
}
},
"@types/ws": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.2.tgz",
"integrity": "sha512-oqnI3DbGCVI9zJ/WHdFo3CUE8jQ8CVQDUIKaDtlTcNeT4zs6UCg9Gvk5QrFx2QPkRszpM6yc8o0p4aGjCsTi+w==",
"requires": {
"@types/node": "*"
}
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
},
"cacheable-lookup": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.0.tgz",
"integrity": "sha512-s2piO6LvA7xnL1AR03wuEdSx3BZT3tIJpZ56/lcJwzO/6DTJZlTs7X3lrvPxk6d1PlDe6PrVe2TjlUIZNFglAQ==",
"requires": {
"keyv": "^4.0.0"
}
},
"cacheable-request": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
"requires": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^4.1.0",
"responselike": "^2.0.0"
}
},
"clone-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
"requires": {
"mimic-response": "^1.0.0"
},
"dependencies": {
"mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
}
}
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"decompress-response": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz",
"integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==",
"requires": {
"mimic-response": "^2.0.0"
}
},
"defer-to-connect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
"integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"discord.js": {
"version": "12.0.2",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.0.2.tgz",
"integrity": "sha512-iZiEA4Y61gqq/EjFfLXnkRK9pLapnax/vTVDUhs/mAhyqozAy0GOlk/MZI9rSa1iIoKTWRq6P9CRKhLNT2wUnA==",
"requires": {
"@discordjs/collection": "^0.1.5",
"abort-controller": "^3.0.0",
"form-data": "^3.0.0",
"node-fetch": "^2.6.0",
"prism-media": "^1.2.0",
"setimmediate": "^1.0.5",
"tweetnacl": "^1.0.3",
"ws": "^7.2.1"
}
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"form-data": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"get-stream": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
"requires": {
"pump": "^3.0.0"
}
},
"got": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/got/-/got-10.6.0.tgz",
"integrity": "sha512-3LIdJNTdCFbbJc+h/EH0V5lpNpbJ6Bfwykk21lcQvQsEcrzdi/ltCyQehFHLzJ/ka0UMH4Slg0hkYvAZN9qUDg==",
"requires": {
"@sindresorhus/is": "^2.0.0",
"@szmarczak/http-timer": "^4.0.0",
"@types/cacheable-request": "^6.0.1",
"cacheable-lookup": "^2.0.0",
"cacheable-request": "^7.0.1",
"decompress-response": "^5.0.0",
"duplexer3": "^0.1.4",
"get-stream": "^5.0.0",
"lowercase-keys": "^2.0.0",
"mimic-response": "^2.1.0",
"p-cancelable": "^2.0.0",
"p-event": "^4.0.0",
"responselike": "^2.0.0",
"to-readable-stream": "^2.0.0",
"type-fest": "^0.10.0"
}
},
"http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"keyv": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.0.tgz",
"integrity": "sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog==",
"requires": {
"json-buffer": "3.0.1"
}
},
"lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"requires": {
"mime-db": "1.43.0"
}
},
"mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
},
"mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"requires": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
}
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"p-cancelable": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz",
"integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg=="
},
"p-event": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-event/-/p-event-4.1.0.tgz",
"integrity": "sha512-4vAd06GCsgflX4wHN1JqrMzBh/8QZ4j+rzp0cd2scXRwuBEv+QR3wrVA5aLhWDLw4y2WgDKvzWF3CCLmVM1UgA==",
"requires": {
"p-timeout": "^2.0.1"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-timeout": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
"integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
"requires": {
"p-finally": "^1.0.0"
}
},
"prism-media": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.1.tgz",
"integrity": "sha512-R3EbKwJiYlTvGwcG1DpUt+06DsxOGS5W4AMEHT7oVOjG93MjpdhGX1whHyjnqknylLMupKAsKMEXcTNRbPe6Vw=="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"responselike": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
"requires": {
"lowercase-keys": "^2.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"to-readable-stream": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz",
"integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w=="
},
"tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"type-fest": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz",
"integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw=="
},
"typescript": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.2.tgz",
"integrity": "sha512-2qj/tYkDPDSVf7JiHanwEBwkhxi7DchFewIsSnR33MQtG3O/BPAJjqs4g6XEuayuRqIExSQMHZlmyDLbuSrXYw=="
}
}
}

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "conjurebot",
"version": "0.0.1",
"description": "Discord bot for Conjure",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"author": "misabiko",
"license": "ISC",
"dependencies": {
"@types/mysql": "^2.15.9",
"@types/node": "^13.9.0",
"@types/ws": "^7.2.2",
"discord.js": "^12.0.2",
"got": "^10.6.0",
"mysql": "^2.18.1",
"typescript": "^3.8.3"
}
}

View File

@ -1,2 +0,0 @@
discord.py
python-dotenv

94
src/Command.ts Normal file
View File

@ -0,0 +1,94 @@
import {Message} from 'discord.js';
import {CustomClient} from "./index";
export class CommandCollection {
private commands : { [commandName : string] : Command } = {};
get(commandName : string, aliased = true) : Command {
let command = this.commands[commandName];
if (!command && aliased)
command = Object.values(this.commands).find(
(cmd : Command) => cmd.checkName(commandName)
) as Command;
return command;
}
has(commandName : string, aliased = true) : boolean {
return !!this.get(commandName, aliased);
}
add(command : Command) {
if (this.commands[command.name])
return console.error(`Command ${command.name} is already in the collection`);
this.commands[command.name] = command;
}
clear(exceptions : Command[]) {
for (const commandName in this.commands)
if (!exceptions.find(cmd => cmd.checkName(commandName)))
delete this.commands[commandName];
}
getNames() {
return Object.keys(this.commands);
}
}
interface Permissions {
users: string[];
groups: string[];
}
export class Command {
name : string;
description? : string;
args? : string[];
guildOnly? : boolean;
cooldown? : number;
aliases? : string[];
usage? : string;
permissions? : Permissions;
constructor(
name : string,
public execute : (message? : Message, args? : string[], client? : CustomClient) => void,
{
description = null as string,
args = null as string[],
guildOnly = false,
cooldown = 0,
aliases = null as string[],
usage = null as string,
permissions = null as Permissions,
}
) {
this.name = name.toLowerCase();
this.description = description;
this.args = args;
this.guildOnly = guildOnly;
this.cooldown = cooldown;
this.aliases = aliases;
this.usage = usage;
this.permissions = permissions;
}
checkName(commandName : string) {
return commandName === this.name || (this.aliases && this.aliases.includes(commandName));
}
checkPermission(userId : string) : boolean {
if (!this.permissions) return true;
if (this.permissions.users.includes(userId))
return true;
for (const group of this.permissions.groups)
if (group.includes(userId))
return true;
return false;
}
}

View File

@ -0,0 +1,74 @@
import {Command} from "../Command";
import got from 'got';
const {userDict, crowd, groupDict} = require("../config.json");
const addcrowduser = new Command(
'addcrowduser',
async (message, args) => {
const email = args[0];
const firstName = args[1];
const lastName = args.splice(2).join(' ');
const firstNameLetter = firstName[0].toLowerCase();
const lastNameLowercase = lastName.toLowerCase().replace(/\s/g, '');;
const username = firstNameLetter + lastNameLowercase;
const password = lastNameLowercase + firstNameLetter;
try {
await got.post("https://conjure.etsmtl.ca/crowd/rest/usermanagement/1/user", {
responseType: 'json',
username: crowd.username,
password: crowd.password,
headers: {
Accept: 'application/json'
},
json: {
"name": username,
"password": {
"value": password
},
"active": true,
"email": email,
"first-name": firstName,
"last-name": lastName
}
});
} catch (error) {
console.error(error.response.body);
message.channel.send("Couldn't add the new member.");
return;
}
try {
await got.post("https://conjure.etsmtl.ca/crowd/rest/usermanagement/1/user/group/direct", {
responseType: 'json',
username: crowd.username,
password: crowd.password,
headers: {
Accept: 'application/json'
},
json: {
name: 'conjure-member'
},
searchParams: new URLSearchParams([
['username', username]
])
});
} catch (error) {
console.error(error.response.body);
message.channel.send("Couldn't add the user to conjure-member.");
return;
}
message.channel.send(`Member added.\nUsername: ${username}\nPassword: ${password}`);
}, {
description: "Adds a member to Crowd.",
permissions: {
users: [],
groups: [groupDict.admin]
},
args: ['email', 'first name', 'last name']
}
);
export default addcrowduser;

12
src/commands/echo.ts Normal file
View File

@ -0,0 +1,12 @@
import {Command} from "../Command";
const echo = new Command(
'echo',
(message, args) => {
message.channel.send(args.join(' '));
},
{
description: "Echo's the arguments as a message"
}
);
export default echo;

13
src/commands/echolog.ts Normal file
View File

@ -0,0 +1,13 @@
import {Command} from "../Command";
const echolog = new Command(
'echolog',
(message, args) => {
console.log("Echolog:");
for (const arg of args)
console.log(arg);
},
{
description: "Echo's the arguments on the console"
});
export default echolog;

42
src/commands/help.ts Normal file
View File

@ -0,0 +1,42 @@
import {Command} from "../Command";
import {CustomClient} from "../index";
const {prefix} = require("../config.json");
const help = new Command(
'help',
(message, args) => {
const data = [];
const {commandCollection} = message.client as CustomClient;
if (!args.length) {
data.push("Here's a list of the commands:");
data.push(commandCollection.getNames().join(", "));
data.push(`\nSend \`${prefix}help [command name]\` for info on a specific command.`);
return message.channel.send(data, {split: true});
}
const name = args[0].toLowerCase();
const command = commandCollection.get(name);
if (!command)
return message.reply(`${name} isn't not a valid command, call ${prefix}help for the list of available commands.`);
data.push(`**Name:** ${command.name}`);
if (command.aliases) data.push(`**Aliases:** ${command.aliases.join(', ')}`);
if (command.description) data.push(`**Description:** ${command.description}`);
if (command.usage) data.push(`**Usage:** ${prefix}${command.name} ${command.usage}`);
data.push(`**Cooldown:** ${command.cooldown || 1} second(s)`);
message.channel.send(data, {split: true});
},
{
description: "List every commands or info about a specific command.",
aliases: ["commands"],
usage: "[command name]"
}
);
export default help;

View File

@ -0,0 +1,51 @@
import {Command} from "../Command";
import { TextChannel } from "discord.js";
import * as mysql from 'mysql';
const {userDict, channels, mysql: mysqlCreds, groupDict} = require("../config.json");
const listdiscordusers = new Command(
'listdiscordusers',
async (message, args, client) => {
const general : TextChannel = await client.channels.fetch(channels.general) as TextChannel;
const members = new Set(general.members.array().map(user => `${user.user.username}#${user.user.discriminator}`));
let dbDiscords;
const connection = mysql.createConnection({
host: 'localhost',
database: 'conjure',
user: mysqlCreds.username,
password: mysqlCreds.password
});
connection.connect();
try {
connection.query('select * from contactDiscord where discord is not null', function (error : any, results : any, fields : any) {
if (error) throw error;
dbDiscords = new Set(results.map((row : any) => row.discord));
for (const member of members.values())
if (dbDiscords.has(member))
members.delete(member);
console.dir(members);
message.channel.send(Array.from(members));
});
connection.end();
} catch (error) {
console.error(error);
message.channel.send("Couldn't query to database.");
return;
}
}, {
description: "Lists discord missing from database.",
permissions: {
users: [],
groups: [groupDict.admin]
}
}
);
export default listdiscordusers;

View File

@ -0,0 +1,61 @@
import * as fs from "fs";
import * as path from "path";
import {Message} from "discord.js";
import {Command} from "../Command";
import {CustomClient} from "../index";
const {userDict} = require("../config.json");
const reloadcommand = new Command(
'reloadcommand',
async (message, args, client) => {
if (args.length)
await reloadSingleCommand(args[0].toLowerCase(), client, message);
else
await reloadCommands(client, message);
}, {
description: "Reloads either every, or a given command module.",
aliases: ["reload"],
usage: "[commandName]",
permissions: {
users: [userDict.misabiko],
groups: []
}
}
);
export default reloadcommand;
async function reloadSingleCommand(commandName : string, client : CustomClient, message : Message) {
const {commandCollection} = client;
if (commandName === 'reloadcommand')
return message.channel.send("You have to restart the bot to reload this command.");
if (!commandCollection.has(commandName))
return message.channel.send(`The ${commandName} command doesn't exist.`);
if (await client.importCommand(`./${commandName}.js`, message))
await message.channel.send(`Successfully reloaded the ${commandName} command.`);
else
await message.channel.send(`Failed to reload the ${commandName} command.`);
}
async function reloadCommands(client : CustomClient, message : Message) {
const {commandCollection} = client;
commandCollection.clear([
commandCollection.get('reloadcommand')
]);
const commandFiles = fs.readdirSync(__dirname).filter(file => file.endsWith(".js"));
let hadErrors = false;
for (const file of commandFiles)
if (file !== "reloadcommand.js")
hadErrors = hadErrors || !(await client.importCommand(path.join(__dirname, file), message));
if (hadErrors)
await message.channel.send(`Failed to reload some commands.`);
else
await message.channel.send(`Successfully reloaded the commands.`);
}

108
src/index.ts Normal file
View File

@ -0,0 +1,108 @@
import * as path from 'path';
import * as fs from 'fs';
import * as Discord from "discord.js";
import {Message, Snowflake} from "discord.js";
import {Command, CommandCollection} from "./Command";
import {prefix, token, userDict} from "./config.json";
export class CustomClient extends Discord.Client {
commandCollection = new CommandCollection();
cooldowns : { [name : string] : Discord.Collection<Snowflake, number> } = {};
prefix = prefix;
async importCommand(commandPath : string, message? : Message) : Promise<boolean> {
try {
this.commandCollection.add((await import(commandPath)).default);
}catch(error) {
const errorMessage = "Error while importing command " + commandPath;
console.error(errorMessage);
console.error(error);
if (message)
await message.reply(errorMessage);
else {
const user = await this.users.fetch(userDict.misabiko);
await user.send(errorMessage);
}
return false;
}
return true;
}
handleMessage(message : Message) {
//If message doesn't have prefix or is bot-made, ignore
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const commandName = args.shift().toLowerCase();
const command = client.commandCollection.get(commandName);
if (!command) return;
if (command.args && !args.length) {
let reply = `You didn't provide any arguments, ${message.author}.`;
if (command.usage)
reply += `\nUsage: \`${prefix}${command.name} ${command.usage}\``;
return message.channel.send(reply);
}
if (command.guildOnly && message.channel.type !== 'text')
return message.reply(`You can only call the ${command} commmand on a server.`);
if (!command.checkPermission(message.author.id))
return message.reply(`You don't have the permission for that command, ask <@${userDict.misabiko}> for help.`);
if (!this.cooldowns[command.name])
this.cooldowns[command.name] = new Discord.Collection();
const timeLeft = this.getTimeLeft(command, message.author.id);
if (timeLeft)
return message.reply(`Please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${command.name}\` command.`);
try {
command.execute(message, args, client);
}catch (error) {
console.error(error);
message.reply("There was an error trying to execute the command.");
}
}
getTimeLeft(command : Command, authorId : string) {
const now = Date.now();
const timestamps : Discord.Collection<Snowflake, number> = this.cooldowns[command.name];
const cooldownAmount = command.cooldown * 1000;
if (!timestamps.has(authorId)) {
timestamps.set(authorId, now);
setTimeout(() => timestamps.delete(authorId), cooldownAmount);
}else {
const expirationTime = timestamps.get(authorId) + cooldownAmount;
if (now < expirationTime)
return (expirationTime - now) / 1000;
timestamps.set(authorId, now);
setTimeout(() => timestamps.delete(authorId), cooldownAmount);
}
}
getCommand(message : string) {
return message.substring(this.prefix.length);
}
}
const client = new CustomClient();
const commandFiles = fs.readdirSync(path.join(__dirname, "commands")).filter((file : string) => file.endsWith(".js"));
for (const file of commandFiles)
client.importCommand(path.join(__dirname, "commands", file)).then();
client.on("message", message => client.handleMessage(message as Message));
client.on("ready", () => console.log("Ready!"));
client.login(token).then(() => console.log("ConjureBot logged in!"));

18
tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ESNext",
"sourceMap": true,
"noImplicitAny": true,
"noImplicitUseStrict": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"outDir": "dist",
"rootDir": "src",
"module": "commonjs"
},
"include": [
"src/*.ts",
"src/commands/*.ts",
"src/config.json"
]
}