-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiscordbase.py
More file actions
157 lines (129 loc) · 5.92 KB
/
Copy pathdiscordbase.py
File metadata and controls
157 lines (129 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# Discord Bot Base
# Author: Nathan Strong
# Date: Nov 1, 2021
# Description: This is not meant to be run in most cases. Instead, it's meant to be imported into other projects (assuming you can even do that) and used to build actual bots that do things that are interesting.
import discord
import sys
import time
# Get the command arguments out of a string, grouping arguments in quotation marks
def get_args(arg_string):
# Initialize some variables
possible_arg_list = arg_string.split()
arg_list = []
current_arg = ""
# Go through the list, collecting all arguments in quotation marks into single arguments
# For instance, hello "world hello" world should become ["hello", "world hello", "world"]
for possible_arg in possible_arg_list:
if current_arg != "":
current_arg = current_arg + " " + possible_arg
if possible_arg.endswith("\""):
arg_list.append(current_arg[:-1])
current_arg = ""
elif possible_arg.startswith("\""):
current_arg = possible_arg[1:]
else:
arg_list.append(possible_arg)
return arg_list
# Returns timestamp (hour:minute:second)
def timestamp():
t = time.localtime()
# time.localtime is off by 5 hours for some reason... guess I need to adjust that...
return f"{(t.tm_hour+5)%24:02d}:{t.tm_min:02d}:{t.tm_sec:02d}"
class CommandData:
def __init__(self, fn, desc, syntax, private):
self.fn = fn
self.desc = desc
self.private = private
self.syntax = syntax
class PermsClass:
def __init__(self):
self.none = "anyone"
self.admin = "a mod"
self.owner = "the owner of this bot"
def verify_perms(self, user, perms, channel, owner):
if perms == self.none or user.id == owner:
return True
elif user.permissions_in(channel).administrator:
if perms == self.admin:
return True
return False
Perms = PermsClass()
# Create a decorator that knows which functions are commands in a given object
all_commands = {}
def command(name, description, syntax=None, private=False, perms_needed=Perms.none):
if syntax == None:
syntax = "{}"+name
def command_decorator(func):
async def wrapper(bot, args, ctx):
if Perms.verify_perms(ctx.author, perms_needed, ctx.channel, bot.owner):
await func(bot, args, ctx)
else:
await ctx.channel.send(f"Insufficient permissions. You need to be {perms_needed} to run this command.")
if name in all_commands.keys():
raise Exception(f"The command name '{name}' was used more than once")
all_commands[name] = CommandData(wrapper, description, syntax, private)
return wrapper
return command_decorator
class BaseBot(discord.Client):
def __init__(self, prefix="!", owner=None, **base_args):
# Run base class __init__ function using any arguments passed in
super().__init__(**base_args)
# Set prefix, commands, owner, last seen message
self.prefix = prefix
self.commands = all_commands
self.owner = owner
self.last_msg = None
async def on_ready(self):
# State that the bot has logged on
print(f"{self.user} connected to discord at {timestamp()}")
# For each server we have a channel for, get the idks in that channel and store them
for guild in self.guilds:
await guild.system_channel.send("I'm online!")
async def on_message(self, message):
# Log the message in the console
if self.last_msg == None:
channel_display = f"{message.guild} (#{message.channel})"
elif self.last_msg.channel == message.channel:
channel_display = "same channel"
elif self.last_msg.guild == message.guild:
channel_display = f"same guild (#{message.channel})"
else:
channel_display = f"{message.guild} (#{message.channel})"
self.last_msg = message
print(f"{message.author} @{timestamp()} in {channel_display}: {message.content}")
# If the message was sent by the bot itself or if it doesn't have the required prefix, return (since the message is not a command)
if message.author == self.user:
return
if not message.content.startswith(self.prefix):
return
# Get the necessary command details, then execute the requested command if it is found.
command = get_args(message.content[len(self.prefix):])
command_name = command[0]
command_args = command[1:]
if command_name in self.commands.keys():
await self.commands[command_name].fn(self, command_args, message)
else:
await message.channel.send(f"Unknown command '{command_name}'. Try '{self.prefix}help' for a list of available commands.")
@command("help", "Sends this message")
async def list_commands(self, args, ctx):
msg = ""
for name, cmd in all_commands.items():
if not cmd.private:
msg = msg + f"""{" ".join(name.capitalize().split("_"))}:
-Description: {cmd.desc}.
-Syntax: {cmd.syntax.format(self.prefix)}
"""
await ctx.channel.send(msg)
@command("stop", "Takes the bot offline. Only usable by the creator of this bot", private=True, perms_needed=Perms.owner)
async def end(self, args, ctx):
# Go offline, end program
for server in self.guilds:
await server.system_channel.send("IDKBot is going offline. This will likely be temporary.")
await ctx.channel.send("Going offline.")
await self.change_presence(status=discord.Status.offline)
sys.exit(f"Stop command run by {ctx.author}, bot offline. This is NOT an error.")
# Main function
def main():
print("Why are you running this? It's a module, not a program.")
if __name__ == "__main__":
main()