Skip to content

Commit 9941ac1

Browse files
author
CreeperSeth
committed
Wish we could turn back time, to the good old days
1 parent 59a51a2 commit 9941ac1

18 files changed

+524
-252
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ __pycache__/
88

99
# Bot files
1010
data/
11+
logs/
1112
config/config.ini
1213

1314
# Windows nonsense

assets/RankSysInfo.txt

-6
This file was deleted.

bot.py

+84-48
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import os
77
import json
88

9+
from datetime import datetime
10+
911
start_time = time.time()
1012

1113
# Initialize the logger first so the colors and shit are setup
@@ -16,6 +18,7 @@
1618
Bootstrap.run_checks()
1719

1820
from utils import checks
21+
from utils import ranking
1922

2023
from discord.ext import commands
2124
from utils.config import Config
@@ -25,21 +28,37 @@
2528
from utils.buildinfo import *
2629

2730
config = Config()
31+
log.setupRotator(config.log_dateformat, config.log_timeformat)
2832
if config.debug:
2933
log.enableDebugging() # pls no flame
3034
bot = commands.Bot(command_prefix=config.command_prefix, description="A multi-purpose Ruby Rose from RWBY themed discord bot", pm_help=True)
3135
channel_logger = Channel_Logger(bot)
3236
aiosession = aiohttp.ClientSession(loop=bot.loop)
3337
lock_status = config.lock_status
3438

35-
extensions = ["commands.fun", "commands.information", "commands.moderation", "commands.configuration", "commands.rwby", "commands.nsfw", "commands.music", "commands.reactions", "commands.economy"]
39+
extensions = ["commands.fun", "commands.information", "commands.moderation", "commands.configuration", "commands.rwby", "commands.nsfw", "commands.music", "commands.reactions", "commands.economy", "commands.ranking"]
3640

3741
# Thy changelog
3842
change_log = [
43+
"Side note: Read r!econotice because I still need suggestions for the eco system.",
3944
"Commands:",
40-
"+ roleid",
45+
"+ editmessage",
46+
"+ rank",
47+
"+ rankuproles",
48+
"+ addrankuprole",
49+
"+ removerankuprole",
50+
"+ topranked",
51+
"+ reverse",
52+
"+ react",
53+
"+ intellect",
4154
"Other things:",
42-
"Instead of defining an nsfw channel, now the channel must be named \"nsfw\" or start with the \"nsfw-\" prefix for nsfw commands to work in them"
55+
"Added the ranking system",
56+
"Reworked the terminal command so it performs better",
57+
"Added a missing invalid type message to the joinleave command",
58+
"Re-worked my check functions to be less confusing",
59+
"- Removed the mod-log channel feature as audit logs are a better alternative. If you wish for users to see audit logs just give them the `View Audit Log` permission.",
60+
"",
61+
"Added a ranking system, it is disabled by default and can be enabled with the config command. You can gain xp from sending messages after a 2 minute interval from recieving xp from a previous message. You can add and remove roles to be added after achieving a certain level with the addrankuprole and the removerankuprole commands."
4362
]
4463

4564
async def _restart_bot():
@@ -128,8 +147,6 @@ async def on_ready():
128147
log.info("Carbonitex stats updated")
129148
else:
130149
log.error("Failed to update the carbonitex stats, double check the key in the config!")
131-
if not os.path.isfile("data/ranksysvotes.json"):
132-
write_file("data/ranksysvotes.json", ["{}"])
133150

134151
@bot.event
135152
async def on_command_error(error, ctx):
@@ -138,9 +155,23 @@ async def on_command_error(error, ctx):
138155
if isinstance(error, commands.DisabledCommand):
139156
await bot.send_message(ctx.message.channel, "This command has been disabled")
140157
return
141-
158+
if isinstance(error, checks.dev_only):
159+
await bot.send_message(ctx.message.channel, "This command can only be ran by the bot developers")
160+
return
161+
if isinstance(error, checks.owner_only):
162+
await bot.send_message(ctx.message.channel, "This command can only be ran by the bot owner")
163+
return
164+
if isinstance(error, checks.not_nsfw_channel):
165+
await bot.send_message(ctx.message.channel, "This command can only be ran in NSFW enabled channels. It must either be named `nsfw` or the name must start with `nsfw-`")
166+
return
167+
if isinstance(error, checks.not_server_owner):
168+
await bot.send_message(ctx.message.channel, "Only the server owner (`{}`) can use this command".format(ctx.message.server.owner))
169+
return
170+
if isinstance(error, checks.no_permission):
171+
await bot.send_message(ctx.message.channel, "You do not have permission to use this command".format(ctx.message.server.owner))
172+
return
142173
if ctx.message.channel.is_private:
143-
await bot.send_message(ctx.message.channel, "An error occured while trying to run this command, this is most likely because it was ran in this private message channel. Please try running this command on a server.")
174+
await bot.send_message(ctx.message.channel, "An error occured while trying to run this command, this is most likely because it was ran in a private message channel. Please try running this command on a server.")
144175
return
145176

146177
# In case the bot failed to send a message to the channel, the try except pass statement is to prevent another error
@@ -156,7 +187,11 @@ async def on_command(command, ctx):
156187
server = "Private Message"
157188
else:
158189
server = "{}/{}".format(ctx.message.server.id, ctx.message.server.name)
159-
print("[Command] [{}] [{}/{}]: {}".format(server, ctx.message.author.id, ctx.message.author, ctx.message.content))
190+
log_line = "[Command] [{}] [{}/{}]: {}".format(server, ctx.message.author.id, ctx.message.author, ctx.message.content)
191+
print(log_line)
192+
log_file = open("logs/latest.log", "a+")
193+
log_file.write("{} {}\n".format(datetime.now().strftime("{} {}".format(config.log_dateformat, config.log_timeformat)), log_line))
194+
log_file.close()
160195

161196
@bot.event
162197
async def on_message(message):
@@ -167,31 +202,19 @@ async def on_message(message):
167202
return
168203
if getblacklistentry(message.author.id) is not None:
169204
return
205+
if not message.channel.is_private:
206+
if read_data_entry(message.server.id, "enable-ranking"):
207+
if ranking.level_up(message.author, message.server):
208+
level = ranking.get_rank_data(message.author, message.server)["level"]
209+
role_rank_ups = ranking.get_rankup_role_dict(message.server)
210+
try:
211+
role = discord.utils.get(message.server.roles, id=role_rank_ups[level])
212+
await bot.add_roles(message.author, role)
213+
except:
214+
pass
215+
await bot.send_message(message.channel, "{} you've leveled up to level **{}**!".format(message.author.mention, level))
170216
await bot.process_commands(message)
171217

172-
@bot.event
173-
async def on_server_update(before:discord.Server, after:discord.Server):
174-
if before.name != after.name:
175-
await channel_logger.mod_log(after, "Server name was changed from `{}` to `{}`".format(before.name, after.name))
176-
if before.region != after.region:
177-
await channel_logger.mod_log(after, "Server region was changed from `{}` to `{}`".format(before.region, after.region))
178-
if before.afk_channel != after.afk_channel:
179-
await channel_logger.mod_log(after, "Server afk channel was changed from `{}` to `{}`".format(before.afk_channel.name, after.afk_channel.name))
180-
if before.afk_timeout != after.afk_timeout:
181-
await channel_logger.mod_log(after, "Server afk timeout was changed from `{}` seconds to `{}` seconds".format(before.afk_timeout, after.afk_timeout))
182-
if before.icon != after.icon:
183-
await channel_logger.mod_log(after, "Server icon was changed from {} to {}".format(before.icon_url, after.icon_url))
184-
if before.mfa_level != after.mfa_level:
185-
if after.mfa_level == 0:
186-
mfa = "enabled"
187-
else:
188-
mfa = "disabled"
189-
await channel_logger.mod_log(after, "Server two-factor authentication requirement has been `{}`".format(mfa))
190-
if before.verification_level != after.verification_level:
191-
await channel_logger.mod_log(after, "Server verification level was changed from `{}` to `{}`".format(before.verification_level, after.verification_level))
192-
if before.owner != after.owner:
193-
await channel_logger.mod_log(after, "Server ownership was transferred from `{}` to `{}`".format(before.owner, after.owner))
194-
195218
@bot.event
196219
async def on_member_join(member:discord.Member):
197220
join_message = read_data_entry(member.server.id, "join-message")
@@ -432,7 +455,7 @@ async def terminal(ctx, *, command:str):
432455
"""Runs terminal commands and shows the output via a message. Oooh spoopy!"""
433456
try:
434457
await bot.send_typing(ctx.message.channel)
435-
await bot.say(xl.format(os.popen(command).read()))
458+
await bot.say(xl.format(subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode("ascii")))
436459
except:
437460
await bot.say("Error, couldn't send command")
438461

@@ -539,6 +562,21 @@ async def stats():
539562
embed.set_footer(text=bot_owner, icon_url=get_avatar(bot_owner))
540563
await bot.say(embed=embed)
541564

565+
@bot.command(pass_context=True)
566+
@checks.is_dev()
567+
async def editmessage(ctx, id:str, *, newmsg:str):
568+
"""Edits a message sent by the bot"""
569+
try:
570+
msg = await bot.get_message(ctx.message.channel, id)
571+
except discord.errors.NotFound:
572+
await bot.say("Couldn't find a message with an ID of `{}` in this channel".format(id))
573+
return
574+
if msg.author != ctx.message.server.me:
575+
await bot.say("That message was not sent by me")
576+
return
577+
await bot.edit_message(msg, newmsg)
578+
await bot.say("edit af")
579+
542580
@bot.command()
543581
async def top10servers():
544582
"""Gets the top 10 most populated servers the bot is on"""
@@ -555,10 +593,11 @@ async def top10servers():
555593
servers.append("{}: {} members, {} bots ({} total)".format(server.name, members, bots, total))
556594
await bot.say("```{}```".format("\n\n".join(servers)))
557595

558-
@bot.command(pass_context=True)
559-
async def ranksysvote(ctx, vote:str):
560-
"""Vote to oppose or object an optional rank system"""
561-
with open("data/ranksysvotes.json", "r") as jsonFile:
596+
@bot.command(pass_context=True, hidden=True, enable=False)
597+
async def vote(ctx, vote:str):
598+
"""Vote command"""
599+
# Left this code here for future purposes so in the event I run another poll I don't need to recode a vote command
600+
with open("data/votes.json", "r") as jsonFile:
562601
votes = json.load(jsonFile)
563602
voted = False
564603
try:
@@ -579,24 +618,21 @@ async def ranksysvote(ctx, vote:str):
579618
with open("data/ranksysvotes.json", "w") as jsonFile:
580619
json.dump(votes, jsonFile)
581620

582-
@bot.command()
621+
@bot.command(hidden=True, enable=False)
583622
async def ranksysvoteresults():
584-
"""Results for the rank system poll"""
585-
with open("data/ranksysvotes.json", "r") as jsonFile:
623+
"""Vote results command"""
624+
# Left this code here for future purposes so in the event I run another poll I don't need to recode a vote results command
625+
with open("data/votes.json", "r") as jsonFile:
586626
votes = json.load(jsonFile)
587-
yes = []
588-
no = []
627+
yes = 0
628+
no = 0
589629
for vote in votes.values():
590630
if vote == "yes":
591-
yes.append(vote)
631+
yes += 1
592632
elif vote == "no":
593-
no.append(vote)
594-
await bot.say("Results for the rank system poll:\nYes: {}\nNo: {}".format(len(yes), len(no)))
633+
no += 1
634+
await bot.say("Results for the rank system poll:\nYes: {}\nNo: {}".format(yes, no))
595635

596-
@bot.command()
597-
async def ranksysinfo():
598-
"""Some info on the ranking system poll"""
599-
await bot.say("".join(open("assets/RankSysInfo.txt", mode="r").readlines()))
600636

601637
print("Connecting...")
602638
bot.run(config._token)

commands/configuration.py

+27-13
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,56 @@
11
from discord.ext import commands
22
from utils.mysql import *
33
from utils.tools import *
4+
from utils import checks
45

56
class Configuration():
67
def __init__(self, bot):
78
self.bot = bot
89

10+
@checks.is_server_owner()
911
@commands.command(pass_context=True)
1012
async def config(self, ctx, type:str, *, value:str):
1113
"""Modifies the server's local config"""
12-
if ctx.message.author is not ctx.message.server.owner:
13-
await self.bot.say("Only the server owner (`{}`) can use this command.".format(ctx.message.server.owner))
14-
return
1514
await self.bot.send_typing(ctx.message.channel)
16-
if type == "mod-role" or type == "mute-role":
17-
if type == "nsfw-channel":
18-
value = value.lower().strip(" ")
19-
update_data_entry(ctx.message.server.id, type, value)
20-
await self.bot.say("Successfully set the {} to `{}`".format(type, value))
15+
if type == "mod-role" or type == "mute-role" or type == "enable-ranking":
16+
if type == "ranking":
17+
try:
18+
global bool
19+
bool = convert_to_bool(value)
20+
update_data_entry(ctx.message.server.id, "ranking", bool)
21+
except ValueError:
22+
await self.bot.say("`{}` is not a valid bool!".format(value))
23+
return
24+
else:
25+
update_data_entry(ctx.message.server.id, type, value)
26+
if type == "enable-ranking":
27+
if bool:
28+
action = "enabled"
29+
else:
30+
action = "disabled"
31+
await self.bot.say("Successfully {} the ranking system".format(action))
32+
else:
33+
await self.bot.say("Successfully set the {} to `{}`".format(type, value))
2134
else:
22-
await self.bot.say("`{}` is not a valid type! Valid types are `mod-role`, and `mute-role`".format(type))
35+
await self.bot.say("`{}` is not a valid type! Valid types are `mod-role`, `mute-role`, and `enable-ranking`".format(type))
2336

2437
@commands.command(pass_context=True)
2538
async def showconfig(self, ctx):
2639
"""Shows the server's configuration"""
2740
await self.bot.send_typing(ctx.message.channel)
2841
mod_role_name = read_data_entry(ctx.message.server.id, "mod-role")
2942
mute_role_name = read_data_entry(ctx.message.server.id, "mute-role")
30-
fields = {"Mod Role":mod_role_name, "Mute Role":mute_role_name}
43+
ranking_enabled = read_data_entry(ctx.message.server.id, "enable-ranking")
44+
fields = {"Mod Role":mod_role_name, "Mute Role":mute_role_name, "Ranking":ranking_enabled}
3145
embed = make_list_embed(fields)
3246
embed.title = "Server Configuration"
3347
embed.color = 0xFF0000
3448
await self.bot.say(embed=embed)
3549

50+
@checks.is_server_owner()
3651
@commands.command(pass_context=True)
3752
async def joinleave(self, ctx, type:str, *, value:str):
3853
"""Configures on user join and leave settings"""
39-
if ctx.message.author is not ctx.message.server.owner:
40-
await self.bot.say("Only the server owner (`{}`) can use this command.".format(ctx.message.server.owner))
41-
return
4254
await self.bot.send_typing(ctx.message.channel)
4355
if type == "join-message":
4456
update_data_entry(ctx.message.server.id, type, value)
@@ -68,6 +80,8 @@ async def joinleave(self, ctx, type:str, *, value:str):
6880
return
6981
update_data_entry(ctx.message.server.id, type, role.id)
7082
await self.bot.say("Successfully set the join-role to: {}".format(role.name))
83+
else:
84+
await self.bot.say("`{}` is not a valid type! Valid types are `join-message`, `leave-message`, `channel`, and `mute-role`".format(type))
7185

7286
@commands.command(pass_context=True)
7387
async def showjoinleaveconfig(self, ctx):

0 commit comments

Comments
 (0)