Mass update to latest discord.py
This commit is contained in:
parent
ace8f0524f
commit
0b530493a7
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,4 +1,2 @@
|
||||
/sqlPass.txt
|
||||
/token.txt
|
||||
/scr/Modules/TMC/ChangeServer/text.txt
|
||||
/rcon.txt
|
||||
/scr/modules/TMC/ChangeServer/text.txt
|
||||
/settings.json
|
||||
|
3
.idea/Discord Bot.iml
generated
3
.idea/Discord Bot.iml
generated
@ -5,8 +5,9 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/scr" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv-linux" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.8 (discord_bot)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.13 (discord_bot)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="R User Library" level="project" />
|
||||
<orderEntry type="library" name="R Skeletons" level="application" />
|
||||
|
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@ -1,4 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (discord_bot)" project-jdk-type="Python SDK" />
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12 (discord_bot)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (discord_bot)" project-jdk-type="Python SDK" />
|
||||
</project>
|
6
.idea/sqldialects.xml
generated
6
.idea/sqldialects.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="PROJECT" dialect="MySQL" />
|
||||
</component>
|
||||
</project>
|
@ -1,4 +1,5 @@
|
||||
# Discord_Bot
|
||||
|
||||
bot.py in root is the main bot file
|
||||
bot.py in /OLD is the bot file before the rewrite
|
||||
/modules is the folder for all the modules
|
||||
|
@ -1,121 +0,0 @@
|
||||
import asyncio
|
||||
|
||||
import discord
|
||||
import pymysql
|
||||
from discord.ext import commands
|
||||
|
||||
password = open("../../../../sqlPass.txt", 'r')
|
||||
|
||||
con = pymysql.connect(host='localhost',
|
||||
user='Bot',
|
||||
password=f'{password}',
|
||||
db='serverIDs',
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
|
||||
class AddServer(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, ctx):
|
||||
if isinstance(ctx.channel, discord.DMChannel) and (ctx.content.lower() == "add server"):
|
||||
try:
|
||||
await ctx.channel.send("Send me an invite to the server. Say ``cancel`` anytime to cancel.")
|
||||
invite = await self.client.wait_for("message", check=lambda message: message.author == ctx.author
|
||||
and message.channel == ctx.channel,
|
||||
timeout=20)
|
||||
invite = await self.client.fetch_invite(invite.content)
|
||||
if invite == "cancel":
|
||||
await ctx.channel.send("Process canceled")
|
||||
return
|
||||
|
||||
categorys = self.client.get_channel(656998225076551680)
|
||||
print(categorys.channels)
|
||||
category_names = ""
|
||||
for channel in categorys.channels:
|
||||
category_names = category_names + '`' + channel.name + '` | '
|
||||
await ctx.channel.send("Please slect a category from the following. This will change how the server"
|
||||
"is shown in the Hub server \n" + category_names)
|
||||
category = await self.client.wait_for("message", check=lambda message: message.author == ctx.author
|
||||
and message.channel == ctx.channel, timeout=20)
|
||||
category = category.content.lower
|
||||
if category == "cancel":
|
||||
await ctx.channel.send("Process canceled")
|
||||
return
|
||||
for channel in categorys.channels:
|
||||
if category == channel.name:
|
||||
pass
|
||||
else:
|
||||
category = "other"
|
||||
|
||||
await ctx.channel.send("Please send a list of tags separated by a "
|
||||
"comma(,) no spaces")
|
||||
tags = await self.client.wait_for("message", check=lambda message: message.author == ctx.author
|
||||
and message.channel == ctx.channel,
|
||||
timeout=20)
|
||||
if tags == "cancel":
|
||||
await ctx.channel.send("Process canceled")
|
||||
return
|
||||
|
||||
await ctx.channel.send("Please send a description of the server")
|
||||
description = await self.client.wait_for("message", check=lambda message: message.author == ctx.author
|
||||
and message.channel == ctx.channel,
|
||||
timeout=20)
|
||||
if description == "cancel":
|
||||
await ctx.channel.send("Process canceled")
|
||||
return
|
||||
|
||||
nsfw = 0
|
||||
for channel in invite.guild.channels:
|
||||
try:
|
||||
if channel.is_nsfw():
|
||||
nsfw = 1
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
cur.execute(f"INSERT INTO servers "
|
||||
f"(ID, name, category, tags, description, nsfw, invite, submitter) "
|
||||
f"VALUES ({invite.guild.id},'"
|
||||
f"{invite.guild.name}','"
|
||||
f"{category}','"
|
||||
f"{tags.content}','"
|
||||
f"{description.content}','"
|
||||
f"{nsfw}','"
|
||||
f"{invite.code}','"
|
||||
f"{ctx.author.id}')")
|
||||
await ctx.channel.send("Server added successfully")
|
||||
|
||||
embed = discord.Embed(
|
||||
colour=discord.Colour.green()
|
||||
)
|
||||
embed.set_author(name="Server Added")
|
||||
embed.add_field(name="ID", value=f"{invite.guild.id}", inline=False)
|
||||
embed.add_field(name="Name", value=f"{invite.guild.name}", inline=False)
|
||||
embed.add_field(name="Tags", value=f"{tags.content}", inline=False)
|
||||
embed.add_field(name="Code", value=f"{invite.code}", inline=False)
|
||||
embed.add_field(name="Description", value=f"{description.content}", inline=False)
|
||||
await self.client.get_channel(658442038475358238).send(embed=embed)
|
||||
|
||||
except Exception as e:
|
||||
await ctx.channel.send("An error has occurred and will be fixed"
|
||||
"shortly.")
|
||||
print(e)
|
||||
await self.client.get_channel(656997125627969546).send(e)
|
||||
await self.client. \
|
||||
get_channel(656997125627969546). \
|
||||
send("<@305589587215122432>")
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.channel.send("Process timeout")
|
||||
return
|
||||
await self.client.process_commands(ctx)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(AddServer(client))
|
@ -1,16 +0,0 @@
|
||||
import pymysql.cursors
|
||||
|
||||
password = open("../../../../sqlPass.txt", 'r')
|
||||
|
||||
con = pymysql.connect(host='localhost',
|
||||
user='Bot',
|
||||
password=f'{password}',
|
||||
db='serverIDs',
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
with con:
|
||||
|
||||
cur = con.cursor()
|
||||
cur.execute("INSERT INTO servers (name, tags, invite, description) VALUES ('Not Very Official Changed Server','furry, rp', 'invitelink.com','N/A')")
|
||||
|
@ -1,14 +0,0 @@
|
||||
import pymysql
|
||||
|
||||
password = open("../../../../sqlPass.txt", 'r')
|
||||
|
||||
con = pymysql.connect(host='localhost',
|
||||
user='Bot',
|
||||
password=f'{password}',
|
||||
db='serverIDs',
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
print(cur.execute("SELECT ID FROM servers WHERE ID = 1"))
|
@ -1 +0,0 @@
|
||||
SELECT ID FROM servers WHERE ID = 1;
|
@ -1,59 +0,0 @@
|
||||
import discord
|
||||
import pymysql
|
||||
from discord.ext import commands
|
||||
|
||||
password = open("../../../../sqlPass.txt", 'r')
|
||||
|
||||
con = pymysql.connect(host='localhost',
|
||||
user='Bot',
|
||||
password=f'{password}',
|
||||
db='serverIDs',
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
|
||||
class RemoveServer(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, ctx):
|
||||
if isinstance(ctx.channel, discord.DMChannel):
|
||||
if ctx.content.lower() == "remove server":
|
||||
await ctx.channel.send("Send the server ID.")
|
||||
server_id = await self.client.wait_for("message", check=lambda message: message.author == ctx.author)
|
||||
try:
|
||||
server_id = int(server_id.content)
|
||||
except ValueError:
|
||||
await ctx.channel.send("Invalid server ID")
|
||||
|
||||
try:
|
||||
member = self.client.get_guild(server_id).get_member(ctx.author.id)
|
||||
if member.guild_permissions.manage_guild:
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
server_exists = cur.execute(f"SELECT ID FROM servers WHERE ID = {server_id}")
|
||||
if server_exists == 0:
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
cur.execute(f"DELETE FROM servers WHERE ID={server_id}")
|
||||
else:
|
||||
await ctx.channel.send("That server could not be found in our databases")
|
||||
|
||||
except AttributeError:
|
||||
await ctx.channel.send("Cannot verify server ownership. "
|
||||
"Either you are not the guild owner or "
|
||||
"the bot has not been added to verify ownership.\n"
|
||||
"Add the bot here : https://discordapp.com/api/oauth2"
|
||||
"/authorize?client_id=501485906801459200&permissions=1024"
|
||||
"&scope=bot\n"
|
||||
"Once the bot is added restart the verification process "
|
||||
"using ``remove server`` in this DM\n"
|
||||
"After ownership is verified the bot can be removed.")
|
||||
await self.client.process_commands(ctx)
|
||||
|
||||
|
||||
# todo
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(RemoveServer(client))
|
@ -1 +0,0 @@
|
||||
|
@ -1,195 +0,0 @@
|
||||
from typing import Union
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import math
|
||||
from datetime import datetime, timedelta
|
||||
import pymysql
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
|
||||
password = open("../sqlPass.txt", 'r').read()
|
||||
|
||||
scheduler = AsyncIOScheduler({
|
||||
'apscheduler.jobstores.default': {
|
||||
'type': 'sqlalchemy',
|
||||
'url': f'mysql+pymysql://quentin:{password}@192.168.1.52:5618/discord_bot?charset=utf8mb4',
|
||||
},
|
||||
'apscheduler.job_defaults.coalesce': 'True',
|
||||
'apscheduler.timezone': 'America/Chicago'
|
||||
})
|
||||
|
||||
scheduler.start()
|
||||
|
||||
mcSelf = None
|
||||
|
||||
|
||||
def get_con() -> pymysql.Connection:
|
||||
return pymysql.connect(host='192.168.1.52',
|
||||
port=5618,
|
||||
user='quentin',
|
||||
password=f'{password}',
|
||||
db='minecraft',
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
|
||||
async def get_poll_results(choices: dict, pollMessageId: int, channelId: int):
|
||||
print("run")
|
||||
|
||||
getPollResultsReactions = (await McRoll.get_message(mcSelf, pollMessageId, channelId)).reactions
|
||||
for reaction in getPollResultsReactions:
|
||||
async for user in reaction.users():
|
||||
serverIP = reaction_to_serverip(reaction)
|
||||
if not user.bot:
|
||||
choices[serverIP]['votes'] = choices[serverIP]['votes'] + 1
|
||||
|
||||
choices[serverIP]['score'] = choices[serverIP]['votes'] * round(
|
||||
2 * math.log10(choices[serverIP]['lastActivated'] + 1) + 1, 2)
|
||||
prune_results(choices)
|
||||
if len(choices) >= 3:
|
||||
await McRoll.main_poll_recursion(mcSelf, choices, channelId)
|
||||
else:
|
||||
await McRoll.declare_winner(mcSelf, choices, channelId)
|
||||
|
||||
|
||||
def reaction_to_serverip(reaction: Union[discord.Reaction, str]) -> int:
|
||||
con = get_con()
|
||||
reaction = str(reaction.emoji.id) \
|
||||
if isinstance(reaction.emoji, discord.Emoji) else reaction.emoji.encode('unicode_escape').decode('utf-8')
|
||||
|
||||
with con.cursor() as cursor:
|
||||
cursor.execute("SELECT serverIP FROM minecraft.server_list WHERE reaction=%s;", reaction)
|
||||
serverIp = cursor.fetchone()
|
||||
con.close()
|
||||
return serverIp['serverIP']
|
||||
|
||||
|
||||
def get_choices() -> dict:
|
||||
con = get_con()
|
||||
choices = {}
|
||||
with con.cursor() as cursor:
|
||||
cursor.execute("SELECT serverIP, serverName, lastActivated, reaction, getEmoji, onlyServer "
|
||||
"FROM minecraft.server_list WHERE lastActivated is not null and permanent=0")
|
||||
row = cursor.fetchone()
|
||||
while row is not None:
|
||||
row['votes'] = 0
|
||||
row['score'] = 0
|
||||
choices[row['serverIP']] = row
|
||||
row = cursor.fetchone()
|
||||
con.close()
|
||||
return choices
|
||||
|
||||
|
||||
def pop_lowest(choices: dict):
|
||||
lowest = {'score': 10}
|
||||
pop = False
|
||||
for choice in list(choices):
|
||||
if choices[choice]['score'] < lowest['score']:
|
||||
lowest = choices[choice]
|
||||
pop = True
|
||||
if pop:
|
||||
choices.pop(lowest['serverIP'])
|
||||
|
||||
|
||||
def prune_results(choices: dict):
|
||||
for serverIp, choice in list(choices.items()):
|
||||
if choice['votes'] <= 0:
|
||||
del choices[serverIp]
|
||||
if len(choices) >= 4:
|
||||
pop_lowest(choices)
|
||||
pop_lowest(choices)
|
||||
elif len(choices) == 3:
|
||||
pop_lowest(choices)
|
||||
|
||||
|
||||
class McRoll(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.embedInfo = """Each server has a multiplier based on how long it has been sense that server has been
|
||||
rolled. When this poll is over all servers with 0 votes will be disregarded. If there are 4 or more servers
|
||||
left the two with the lowest votes will be disregarded. Poll is rerun until there are only 2 servers,
|
||||
these two are the winners. Ties reroll the poll. You can only vote for one server per poll. """
|
||||
self.pollMessageId = None
|
||||
global mcSelf
|
||||
mcSelf = self # This is a bad way of doing things but it works
|
||||
|
||||
@commands.command()
|
||||
async def mc_roll(self, ctx):
|
||||
if ctx.message.author.id != 305589587215122432:
|
||||
return
|
||||
choices = get_choices()
|
||||
await self.main_poll_recursion(choices, ctx.channel.id)
|
||||
|
||||
async def main_poll_recursion(self, choices, channelId):
|
||||
self.pollMessageId = (await self.poll(choices, channelId)).id
|
||||
scheduler.add_job(get_poll_results, 'date',
|
||||
run_date=(datetime.now() + timedelta(seconds=6)),
|
||||
args=[choices, self.pollMessageId, channelId],
|
||||
coalesce=True)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_reaction_add(self, reaction, user):
|
||||
if self.pollMessageId is None:
|
||||
return
|
||||
reactedUsers = {}
|
||||
if reaction.message.id == self.pollMessageId and user.id != 533427166193385494:
|
||||
for iReaction in reaction.message.reactions:
|
||||
async for iUser in iReaction.users():
|
||||
if iUser.id != 533427166193385494:
|
||||
if iUser.id not in reactedUsers:
|
||||
reactedUsers[iUser.id] = False
|
||||
if reactedUsers[iUser.id]:
|
||||
await reaction.remove(user)
|
||||
self.pollMessageId = reaction.message.id
|
||||
await user.send(
|
||||
"You can only vote for one option, please remove your previous vote to change it.")
|
||||
return
|
||||
self.pollMessageId = reaction.message.id
|
||||
reactedUsers[iUser.id] = True
|
||||
|
||||
async def poll(self, choices: dict, channel: int) -> discord.Message:
|
||||
channel = await self.client.fetch_channel(channel)
|
||||
embed = discord.Embed(title="Poll", description=self.embedInfo,
|
||||
timestamp=(datetime.utcnow() + timedelta(days=1)))
|
||||
for choice in choices.values():
|
||||
embed.add_field(
|
||||
name=(choice['serverName'] + ' ' +
|
||||
(str(self.client.get_emoji(int(choice['reaction'])))
|
||||
if choice['getEmoji']
|
||||
else bytes(choice['reaction'], "utf-8").decode("unicode_escape"))),
|
||||
value=("Multiplier : " + str(round(2 * math.log10(choice['lastActivated'] + 1) + 1, 2)) +
|
||||
'\n' + "Last Rolled : " + str(choice['lastActivated'] * 2) + " weeks ago." +
|
||||
("\nThis is an only server, meaning if it wins it will be the only winner" if choice['onlyServer']
|
||||
else " ")))
|
||||
|
||||
pollMessage = await channel.send(embed=embed)
|
||||
await channel.send('@everyone')
|
||||
|
||||
for choice in choices.values():
|
||||
await pollMessage.add_reaction(self.client.get_emoji(int(choice['reaction']))
|
||||
if choice['getEmoji']
|
||||
else bytes(choice['reaction'], "utf-8").decode("unicode_escape"))
|
||||
return pollMessage
|
||||
|
||||
async def declare_winner(self, choices, channelId):
|
||||
embed = discord.Embed(title="Winner", description="Congratulations")
|
||||
for item in list(choices):
|
||||
winner = choices[item]
|
||||
embed.add_field(
|
||||
name=winner['serverName'],
|
||||
value="Multiplier : " + str(round(2 * math.log10(winner['lastActivated'] + 1) + 1, 2)) + '\n' +
|
||||
"Last Rolled : " + str(winner['lastActivated'] * 2) + " weeks ago." + '\n' +
|
||||
"Votes : " + str(winner['votes']) + '\n' +
|
||||
"Calculated Votes: " + str(
|
||||
winner['votes'] * round(2 * math.log10(winner['lastActivated'] + 1) + 1, 2))
|
||||
)
|
||||
channel = await self.client.fetch_channel(channelId)
|
||||
await channel.send(embed=embed)
|
||||
await channel.send("@everyone")
|
||||
|
||||
async def get_message(self, messageId, channelId):
|
||||
channel = await self.client.fetch_channel(channelId)
|
||||
return await channel.fetch_message(messageId)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(McRoll(client))
|
@ -1,23 +0,0 @@
|
||||
import re
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
class ParseForIssues(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.id == 533427166193385494:
|
||||
return
|
||||
content = message.content.lower()
|
||||
matches = re.findall("ttg-[0-9]+", content)
|
||||
for match in matches:
|
||||
await message.channel.send("https://youtrack.themissingcrowbar.com:8942/issue/"+match)
|
||||
|
||||
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(ParseForIssues(client))
|
@ -1,38 +0,0 @@
|
||||
import asyncio
|
||||
import time
|
||||
import logging
|
||||
from datetime import datetime, timedelta, date
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from apscheduler.schedulers.blocking import BlockingScheduler
|
||||
|
||||
scheduler = AsyncIOScheduler({
|
||||
'apscheduler.jobstores.default': {
|
||||
'type': 'sqlalchemy',
|
||||
'url': f'mysql+pymysql://Quentin:kaPl0wskii@192.168.1.52:5618/discord_bot?charset=utf8mb4'
|
||||
},
|
||||
'apscheduler.executors.default': {
|
||||
'class': 'apscheduler.executors.pool:ThreadPoolExecutor',
|
||||
'max_workers': '20'
|
||||
},
|
||||
'apscheduler.executors.processpool': {
|
||||
'type': 'processpool',
|
||||
'max_workers': '5'
|
||||
},
|
||||
'apscheduler.job_defaults.coalesce': 'true',
|
||||
'apscheduler.job_defaults.max_instances': '3',
|
||||
'apscheduler.timezone': 'America/Chicago',
|
||||
})
|
||||
|
||||
scheduler.start()
|
||||
|
||||
async def hello():
|
||||
print("hello world")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# scheduler.add_job(hello, 'date', run_date=datetime.now()+timedelta(seconds=20))
|
||||
|
||||
try:
|
||||
asyncio.get_event_loop().run_forever()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
@ -1,36 +0,0 @@
|
||||
import random
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
content = [", was just chosen to be the one.",
|
||||
", has joined!",
|
||||
", looks like someone took the slow train from Philly.",
|
||||
", just bought the house!",
|
||||
", has a new home.",
|
||||
", if in ten years i haven't had a baby and you haven't had a baby?",
|
||||
" is hear to play minecraft and get killed by my sword!",
|
||||
", is most definitely a furry.",
|
||||
", did you bring your sword?",
|
||||
", welcome!"]
|
||||
|
||||
|
||||
class Join(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_join(self, member):
|
||||
if member.guild.id == 605932352736067585:
|
||||
if member.id == 234703033924059138:
|
||||
await self.client.get_channel(605932352736067589).send(
|
||||
member.mention +
|
||||
", is most definitely both a furry and a weeb. We should all bully him because he stinks."
|
||||
)
|
||||
else:
|
||||
await self.client.get_channel(605932352736067589).send(member.mention +
|
||||
random.choice(content) + "[Join]")
|
||||
await member.add_roles(self.client.get_guild(605932352736067585).get_role(605934866676056070))
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Join(client))
|
@ -1,26 +0,0 @@
|
||||
import random
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
content = [" just bit the dust.",
|
||||
" gave up on life.",
|
||||
" couldn't take the heat anymore.",
|
||||
" boarded the train.",
|
||||
" was pricked to death.",
|
||||
" just left.",
|
||||
" ragequit.",
|
||||
" doesn't like me anymore"]
|
||||
|
||||
|
||||
class Join(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_remove(self, member):
|
||||
if member.guild.id == 605932352736067585:
|
||||
await self.client.get_channel(605932352736067589).send(member.mention + random.choice(content) + "[Leave")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Join(client))
|
@ -1,31 +0,0 @@
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
class Spam(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
dump = []
|
||||
print(len(dump))
|
||||
print((dump[len(dump)-1].created_at - dump[1].created_at).total_seconds())
|
||||
async for temp in message.channel.history(limit=5):
|
||||
if temp.author == message.author:
|
||||
dump.append(temp)
|
||||
if (dump[len(dump)-1].created_at - dump[1].created_at).total_seconds() <= 2:
|
||||
await message.delete()
|
||||
await self.client.process_commands(message)
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Spam(client))
|
||||
|
||||
|
||||
""" async for tempMessage in message.channel.history(limit=5):
|
||||
if tempMessage.id == message.id:
|
||||
pass
|
||||
elif tempMessage.author == message.author:
|
||||
difference = message.created_at - tempMessage.created_at
|
||||
if difference.total_seconds() <= 2:
|
||||
await message.delete()"""
|
@ -1,20 +0,0 @@
|
||||
import asyncio
|
||||
import math
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import discord
|
||||
import pymysql
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
class doym(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.command()
|
||||
async def doymCMD(self, ctx):
|
||||
await ctx.channel.send("https://www.youtube.com/watch?v=5t53TcKIlMc")
|
||||
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(doym(client))
|
100
scr/config.py
Normal file
100
scr/config.py
Normal file
@ -0,0 +1,100 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class MysqlSettings:
|
||||
user: str
|
||||
password: str
|
||||
host: str
|
||||
port: int
|
||||
db: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class BotSettings:
|
||||
token: str
|
||||
guildId: int
|
||||
prefix: str
|
||||
|
||||
@dataclass
|
||||
class RoleSettings:
|
||||
member: str
|
||||
|
||||
roles: BotSettings.RoleSettings
|
||||
|
||||
@dataclass
|
||||
class ChannelSettings:
|
||||
joinLog: int
|
||||
leaveLog: int
|
||||
|
||||
channels: BotSettings.ChannelSettings
|
||||
|
||||
class CommandSettings:
|
||||
def load(self, data):
|
||||
pass
|
||||
|
||||
commands: dict[str, CommandSettings]
|
||||
|
||||
|
||||
@dataclass
|
||||
class YoutrackSettings:
|
||||
url: str
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, configFile: str = "./settings.json"):
|
||||
self.configFile = configFile
|
||||
|
||||
with open(self.configFile, 'r') as file:
|
||||
self.configData = json.loads(file.read())
|
||||
|
||||
self.__load_mysql()
|
||||
self.__load_bot()
|
||||
self.__load_youtrack()
|
||||
|
||||
def register_command_settings(self, settingJson: str):
|
||||
def decorator(cls: type[BotSettings.CommandSettings]):
|
||||
currentSettings = self.BOT.commands.get(settingJson)
|
||||
if currentSettings is not None:
|
||||
raise Exception(f"Setting {settingJson} already loaded.")
|
||||
instance = cls()
|
||||
instance.load(self.commandData[settingJson])
|
||||
self.BOT.commands[settingJson] = instance
|
||||
|
||||
return decorator
|
||||
|
||||
def __load_mysql(self):
|
||||
mysqlData = self.configData["mysql"]
|
||||
self.MYSQL = MysqlSettings(
|
||||
user=mysqlData["user"],
|
||||
password=mysqlData["password"],
|
||||
host=mysqlData["host"],
|
||||
port=mysqlData["port"],
|
||||
db=mysqlData["db"]
|
||||
)
|
||||
|
||||
def __load_bot(self):
|
||||
botData = self.configData["bot"]
|
||||
self.BOT = BotSettings(
|
||||
token=botData["token"],
|
||||
guildId=botData["guild"],
|
||||
prefix=botData["prefix"],
|
||||
roles=BotSettings.RoleSettings(
|
||||
member=botData["roles"]["member"]
|
||||
),
|
||||
channels=BotSettings.ChannelSettings(
|
||||
joinLog=botData["channels"]["joinLog"],
|
||||
leaveLog=botData["channels"]["leaveLog"]
|
||||
),
|
||||
commands={}
|
||||
)
|
||||
self.commandData = botData["commands"]
|
||||
|
||||
def __load_youtrack(self):
|
||||
youtrackData = self.configData["youtrack"]
|
||||
self.YOUTRACK = YoutrackSettings(
|
||||
url=youtrackData["url"]
|
||||
)
|
15
scr/main.py
Normal file
15
scr/main.py
Normal file
@ -0,0 +1,15 @@
|
||||
from config import Config
|
||||
from myBot import MyBot
|
||||
|
||||
config = Config()
|
||||
bot = MyBot(config=config)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print(f'Bot started as {bot.user}')
|
||||
|
||||
modules = ["modules.youtrackIntegration",
|
||||
"modules.TMC"]
|
||||
for module in modules:
|
||||
await bot.load_extension(module)
|
9
scr/modules/TMC/__init__.py
Normal file
9
scr/modules/TMC/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from myBot import MyBot
|
||||
|
||||
cogs = [".autoReply",
|
||||
".commands"]
|
||||
|
||||
|
||||
async def setup(bot: MyBot):
|
||||
for cog in cogs:
|
||||
await bot.load_extension(cog, package="modules.TMC")
|
9
scr/modules/TMC/autoReply/__init__.py
Normal file
9
scr/modules/TMC/autoReply/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from myBot import MyBot
|
||||
|
||||
cogs = [".join",
|
||||
".leave"]
|
||||
|
||||
|
||||
async def setup(bot: MyBot):
|
||||
for cog in cogs:
|
||||
await bot.load_extension(cog, package=__package__)
|
37
scr/modules/TMC/autoReply/join.py
Normal file
37
scr/modules/TMC/autoReply/join.py
Normal file
@ -0,0 +1,37 @@
|
||||
import random
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
from myBot import MyBot
|
||||
|
||||
content = [", was just chosen to be the one.",
|
||||
", has joined!",
|
||||
", looks like someone took the slow train from Philly.",
|
||||
", just bought the house!",
|
||||
", has a new home.",
|
||||
", if in ten years I haven't had a baby and you haven't had a baby?",
|
||||
" is hear to play minecraft and get killed by my sword!",
|
||||
", is most definitely a furry.",
|
||||
", did you bring your sword?",
|
||||
", welcome!",
|
||||
", two trucks?",
|
||||
", *Who needs atom clamps! I have a funny robot!!",
|
||||
" DO NOT POST FNF MUCKBANG.",
|
||||
" just joined, hmm. *sniffs*. Eww.",
|
||||
", hold by pee, I've gotta beer."]
|
||||
|
||||
|
||||
class Join(commands.Cog):
|
||||
def __init__(self, bot: MyBot):
|
||||
self.client = bot
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_join(self, member):
|
||||
if member.guild.id == 605932352736067585:
|
||||
await self.client.get_channel(605932352736067589).send("[Join] " + member.mention +
|
||||
random.choice(content))
|
||||
await member.add_roles(self.client.get_guild(605932352736067585).get_role(605934866676056070))
|
||||
|
||||
|
||||
async def setup(bot: MyBot):
|
||||
await bot.add_cog(Join(bot))
|
36
scr/modules/TMC/autoReply/leave.py
Normal file
36
scr/modules/TMC/autoReply/leave.py
Normal file
@ -0,0 +1,36 @@
|
||||
import random
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
from myBot import MyBot
|
||||
|
||||
content = [" just bit the dust.",
|
||||
" gave up on life.",
|
||||
" couldn't take the heat anymore.",
|
||||
" boarded the train.",
|
||||
" was pricked to death.",
|
||||
" just left.",
|
||||
" ragequit.",
|
||||
" doesn't like me anymore.",
|
||||
" chickened out.",
|
||||
", you're*",
|
||||
" held the pee.",
|
||||
", Shit & Pants ~Jack Black",
|
||||
" rolled off with the root beer.",
|
||||
" doesn't feel like it anymore.",
|
||||
" didn't install the microwave ovens.",
|
||||
" was slower than an i7 Q 740m @ 1.73GHz!"]
|
||||
|
||||
|
||||
class Leave(commands.Cog):
|
||||
def __init__(self, bot: MyBot):
|
||||
self.client = bot
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_remove(self, member):
|
||||
if member.guild.id == 605932352736067585:
|
||||
await self.client.get_channel(605932352736067589).send("[Leave] " + member.mention + random.choice(content))
|
||||
|
||||
|
||||
async def setup(myBot: MyBot):
|
||||
await myBot.add_cog(Leave(myBot))
|
8
scr/modules/TMC/commands/__init__.py
Normal file
8
scr/modules/TMC/commands/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
from myBot import MyBot
|
||||
|
||||
cogs = [".mcRoll"]
|
||||
|
||||
|
||||
async def setup(bot: MyBot):
|
||||
for cog in cogs:
|
||||
await bot.load_extension(cog, package=__package__)
|
226
scr/modules/TMC/commands/mcRoll.py
Normal file
226
scr/modules/TMC/commands/mcRoll.py
Normal file
@ -0,0 +1,226 @@
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta, timezone, date
|
||||
|
||||
import discord
|
||||
import pymysql
|
||||
import pymysql.cursors
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
from tzlocal import get_localzone_name
|
||||
|
||||
from config import BotSettings
|
||||
from myBot import MyBot
|
||||
|
||||
|
||||
@dataclass
|
||||
class PollChoice:
|
||||
lastActivated: date
|
||||
votes: int
|
||||
score: float
|
||||
reaction: str
|
||||
serverName: str
|
||||
getEmoji: int
|
||||
|
||||
bot_instance_registry = {}
|
||||
|
||||
async def poll_result_dispatcher(class_name, instance_id, *args):
|
||||
instance = bot_instance_registry.get((class_name, instance_id))
|
||||
if instance:
|
||||
await instance.get_poll_results(*args)
|
||||
else:
|
||||
print(f"No instance found for {class_name}:{instance_id}")
|
||||
|
||||
|
||||
def get_multiplier(choice: PollChoice):
|
||||
activatedNWeeksAgo = (datetime.now().date() - choice.lastActivated).days / 7
|
||||
return round(1.7 * math.log10(activatedNWeeksAgo + 1) + 1, 2)
|
||||
|
||||
|
||||
def pop_lowest(choices: dict[int, PollChoice]):
|
||||
if not choices:
|
||||
return
|
||||
|
||||
pop = min(choices.items(), key=lambda item: item[1].score)[0]
|
||||
del choices[pop]
|
||||
|
||||
|
||||
def find_choice_by_reaction(choices: dict[int, PollChoice], reaction: discord.Reaction) -> PollChoice | None:
|
||||
for choice in choices.values():
|
||||
if choice.reaction == str(reaction):
|
||||
return choice
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
class McRoll(commands.Cog):
|
||||
def __init__(self, bot: MyBot):
|
||||
self.bot = bot
|
||||
self.embedInfo = (f"Each server has a multiplier based on how long it has been sense that server has been rolled. "
|
||||
f"When this poll is over all servers with 0 votes will be disregarded. "
|
||||
f"1/3rd of the servers with the lowest votes will be disregarded. "
|
||||
f"If there are 4 or more servers Poll is rerun until there are only {self.bot.config.BOT.commands["mc_roll"].pollRuntimeHours} servers, "
|
||||
f"these are the winners. Ties reroll the poll. You can only vote for one server per poll. ")
|
||||
self.pollMessageId = None
|
||||
bot_instance_registry[('McRoll', id)] = self
|
||||
|
||||
mysqlConf = self.bot.config.MYSQL
|
||||
self.scheduler = AsyncIOScheduler({
|
||||
'apscheduler.jobstores.default': {
|
||||
'type': 'sqlalchemy',
|
||||
'url': f'mysql+pymysql://{mysqlConf.user}:{mysqlConf.password}@{mysqlConf.host}:{mysqlConf.port}/{mysqlConf.db}?charset=utf8mb4',
|
||||
},
|
||||
'apscheduler.job_defaults.coalesce': 'True',
|
||||
'apscheduler.timezone': get_localzone_name()
|
||||
})
|
||||
|
||||
self.scheduler.start()
|
||||
|
||||
def prune_results(self, choices: dict[int, PollChoice]):
|
||||
for key, choice in list(choices.items()):
|
||||
if choice.votes <= 0:
|
||||
del choices[key]
|
||||
amountToPop = round(len(choices) * .33)
|
||||
if (len(choices) - amountToPop) <= self.bot.config.BOT.commands["mc_roll"].runningServers:
|
||||
for i in range(len(choices) - self.bot.config.BOT.commands["mc_roll"].runningServers):
|
||||
pop_lowest(choices)
|
||||
return
|
||||
for i in range(amountToPop):
|
||||
pop_lowest(choices)
|
||||
|
||||
def get_con(self) -> pymysql.Connection:
|
||||
mysqlConf = self.bot.config.MYSQL
|
||||
return pymysql.connect(host=mysqlConf.host,
|
||||
port=mysqlConf.port,
|
||||
user=mysqlConf.user,
|
||||
password=mysqlConf.password,
|
||||
db=mysqlConf.db,
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
async def get_poll_results(self, choices: dict[int, PollChoice], pollMessageId: int, channelId: int):
|
||||
reactions = (await self.get_message(pollMessageId, channelId)).reactions
|
||||
for reaction in reactions:
|
||||
async for user in reaction.users():
|
||||
choice = find_choice_by_reaction(choices, reaction)
|
||||
if choice is None:
|
||||
continue
|
||||
if not user.bot:
|
||||
choice.votes = choice.votes + 1
|
||||
|
||||
choice.score = choice.votes * get_multiplier(choice)
|
||||
self.prune_results(choices)
|
||||
if len(choices) > self.bot.config.BOT.commands["mc_roll"].runningServers:
|
||||
await self.main_poll_recursion(choices, channelId)
|
||||
else:
|
||||
await self.declare_winner(choices, channelId)
|
||||
|
||||
def get_choices(self) -> dict[int, PollChoice]:
|
||||
with self.get_con() as con:
|
||||
choices: dict[int, PollChoice] = {}
|
||||
with con.cursor() as cursor:
|
||||
cursor.execute("SELECT id, serverName, lastActivated, reaction, getEmoji FROM server_list WHERE disabled=false")
|
||||
records = cursor.fetchall()
|
||||
for row in records:
|
||||
choice = PollChoice(
|
||||
lastActivated=datetime.now().date() if row["lastActivated"] is None else row["lastActivated"],
|
||||
votes=0,
|
||||
score=0,
|
||||
reaction=row["reaction"],
|
||||
serverName=row["serverName"],
|
||||
getEmoji=row["getEmoji"]
|
||||
)
|
||||
choices[row["id"]] = choice
|
||||
return choices
|
||||
|
||||
@commands.command()
|
||||
async def mc_roll(self, ctx: Context):
|
||||
if not ctx.author.guild_permissions.administrator:
|
||||
return
|
||||
choices = self.get_choices()
|
||||
await self.main_poll_recursion(choices, ctx.channel.id)
|
||||
|
||||
async def main_poll_recursion(self, choices, channelId):
|
||||
self.pollMessageId = (await self.poll(choices, channelId)).id
|
||||
self.scheduler.add_job(poll_result_dispatcher, 'date',
|
||||
run_date=(datetime.now() + timedelta(self.bot.config.BOT.commands["mc_roll"].pollRuntimeHours)),
|
||||
args=['McRoll', id, choices, self.pollMessageId, channelId],
|
||||
coalesce=True)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_reaction_add(self, reaction, user):
|
||||
if self.pollMessageId is None:
|
||||
return
|
||||
reactedUsers = {}
|
||||
if reaction.message.id == self.pollMessageId and user.id != self.bot.user.id:
|
||||
for iReaction in reaction.message.reactions:
|
||||
async for iUser in iReaction.users():
|
||||
if iUser.id != self.bot.user.id:
|
||||
if iUser.id not in reactedUsers:
|
||||
reactedUsers[iUser.id] = False
|
||||
if reactedUsers[iUser.id]:
|
||||
await reaction.remove(user)
|
||||
self.pollMessageId = reaction.message.id
|
||||
await user.send(
|
||||
"You can only vote for one option, please remove your previous vote to change it.")
|
||||
return
|
||||
self.pollMessageId = reaction.message.id
|
||||
reactedUsers[iUser.id] = True
|
||||
|
||||
async def poll(self, choices: dict[int, PollChoice], channel: int) -> discord.Message:
|
||||
channel = await self.bot.fetch_channel(channel)
|
||||
embed = discord.Embed(title="Poll", description=self.embedInfo,
|
||||
timestamp=(datetime.now(timezone.utc) + timedelta(days=1)))
|
||||
for choice in choices.values():
|
||||
embed.add_field(
|
||||
name=(choice.serverName + ' ' +
|
||||
(str(self.bot.get_emoji(int(choice.reaction)))
|
||||
if choice.getEmoji
|
||||
else choice.reaction)),
|
||||
value=("Multiplier : " + str(get_multiplier(choice)) +
|
||||
'\n' + "Last Rolled : " + str(
|
||||
round((datetime.now().date() - choice.lastActivated).days)) + " days ago."))
|
||||
|
||||
pollMessage = await channel.send(embed=embed)
|
||||
await channel.send('@everyone')
|
||||
|
||||
for choice in choices.values():
|
||||
await pollMessage.add_reaction(self.bot.get_emoji(int(choice.reaction))
|
||||
if choice.getEmoji
|
||||
else choice.reaction)
|
||||
return pollMessage
|
||||
|
||||
async def declare_winner(self, choices: dict[int, PollChoice], channelId):
|
||||
embed = discord.Embed(title="Winner", description="Congratulations")
|
||||
for choice in choices.values():
|
||||
embed.add_field(
|
||||
name=choice.serverName,
|
||||
value=("Multiplier : " + str(get_multiplier(choice)) + '\n' +
|
||||
"Last Rolled : " + str(
|
||||
round((datetime.now().date() - choice.lastActivated).days)) + " days ago." + '\n' +
|
||||
"Votes : " + str(choice.votes) + '\n' +
|
||||
"Calculated Votes: " + str(choice.score))
|
||||
)
|
||||
channel = await self.bot.fetch_channel(channelId)
|
||||
await channel.send(embed=embed)
|
||||
await channel.send("@everyone")
|
||||
|
||||
async def get_message(self, messageId, channelId):
|
||||
channel = await self.bot.fetch_channel(channelId)
|
||||
return await channel.fetch_message(messageId)
|
||||
|
||||
|
||||
async def setup(bot: MyBot):
|
||||
@bot.config.register_command_settings("mc_roll")
|
||||
class McRollSettings(BotSettings.CommandSettings):
|
||||
def __init__(self):
|
||||
self.runningServers: int = -1
|
||||
self.pollRuntimeHours: int = -1
|
||||
|
||||
def load(self, data):
|
||||
self.runningServers = data["runningServers"]
|
||||
self.pollRuntimeHours = data["pollRuntimeHours"]
|
||||
|
||||
await bot.add_cog(McRoll(bot))
|
8
scr/modules/youtrackIntegration/__init__.py
Normal file
8
scr/modules/youtrackIntegration/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
from myBot import MyBot
|
||||
|
||||
cogs = [".parseForIssues"]
|
||||
|
||||
|
||||
async def setup(bot: MyBot):
|
||||
for cog in cogs:
|
||||
await bot.load_extension(cog, package=__package__)
|
15
scr/modules/youtrackIntegration/api.py
Normal file
15
scr/modules/youtrackIntegration/api.py
Normal file
@ -0,0 +1,15 @@
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
from config import YoutrackSettings
|
||||
|
||||
|
||||
class Api:
|
||||
def __init__(self, settings: YoutrackSettings):
|
||||
self.URL = settings.url
|
||||
|
||||
def get_projects(self):
|
||||
r = requests.get(self.URL + "api/admin/projects?fields=id,shortName")
|
||||
data = json.loads(r.content)
|
||||
return data
|
32
scr/modules/youtrackIntegration/parseForIssues.py
Normal file
32
scr/modules/youtrackIntegration/parseForIssues.py
Normal file
@ -0,0 +1,32 @@
|
||||
import re
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
from myBot import MyBot
|
||||
from .api import Api
|
||||
|
||||
|
||||
class ParseForIssues(commands.Cog):
|
||||
def __init__(self, bot: MyBot):
|
||||
self.bot = bot
|
||||
self.shortProjectNames = None
|
||||
self.api = Api(bot.config.YOUTRACK)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.id == self.bot.user.id:
|
||||
return
|
||||
content = message.content.lower()
|
||||
if self.shortProjectNames is None:
|
||||
projects = self.api.get_projects()
|
||||
self.shortProjectNames = []
|
||||
for project in projects:
|
||||
self.shortProjectNames.append(project["shortName"].lower())
|
||||
|
||||
matches = re.findall(rf"(?:{'|'.join(map(re.escape, self.shortProjectNames))})-[0-9]+", content)
|
||||
for match in matches:
|
||||
await message.channel.send(self.api.URL + "/issue/" + match)
|
||||
|
||||
|
||||
async def setup(bot: MyBot):
|
||||
await bot.add_cog(ParseForIssues(bot))
|
29
scr/myBot.py
Normal file
29
scr/myBot.py
Normal file
@ -0,0 +1,29 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
from config import Config
|
||||
|
||||
|
||||
class MyBot(commands.Bot):
|
||||
def __init__(self, config: Config):
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
intents.members = True
|
||||
|
||||
super().__init__(command_prefix=config.BOT.prefix, intents=intents)
|
||||
self.config: Config = config
|
||||
|
||||
super().run(self.config.BOT.token)
|
||||
|
||||
async def setup_hook(self) -> None:
|
||||
modules = [
|
||||
"modules.youtrackIntegration",
|
||||
"modules.TMC"
|
||||
]
|
||||
for module in modules:
|
||||
await self.load_extension(module)
|
||||
|
||||
return await super().setup_hook()
|
||||
|
||||
async def on_ready(self):
|
||||
print(f'Bot started as {self.user}')
|
Loading…
x
Reference in New Issue
Block a user