-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbot.py
255 lines (198 loc) · 7.17 KB
/
bot.py
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# Work with Python 3.6
####################
# Imports
####################
import json
import io
import os
import sys
import discord
from discord.ext import commands
from discord import File
import terminal
import logger
#####################
# Global
#####################
with open("keys.json") as json_file:
data = json.load(json_file)
TOKEN = data["discordKey"]
logger = logger.Logger("app.log")
client = commands.Bot(command_prefix = "!")
# CREATE testTerminal for the bash and cwd commands
testTerminal = terminal.Terminal("test1", "/bin/bash")
####################
# Helper methods
####################
def remove_command_prefix(operand: str):
""" Removes however many characters are necessary to remove the
command prefix from operand.
"""
newOperand = operand[len(client.command_prefix):len(operand)]
return newOperand
def parse_command(messageContent: str):
""" Takes messageContent and splits it into two items.
0: the command (prefixless) - str
1: the operands (or an empty string if none are present) - list
These items are then joined into a list and returned to the
caller.
"""
splitMessage = messageContent.split(" ")
command = remove_command_prefix(splitMessage[0])
operands = ""
if len(splitMessage) > 1:
operands = splitMessage[1:len(splitMessage)]
return [command, operands]
#####################
# Events
#####################
@client.event
async def on_ready():
""" Displays to the console that the client has successfully logged
in.
"""
print("Logged in as")
print(client.user.name)
print(client.user.id)
print("------")
@client.event
async def on_command(ctx):
""" Whenever a command is called, we want to log:
WHO called it
WHAT they called (which command)
HOW they called it (what operands)
IF AND ONLY IF there are operands,
we want to send to the console and logger said operands.
"""
splitMessage = parse_command(ctx.message.content)
command = splitMessage[0]
operands = splitMessage[1]
userAction = "{0.author} calls {1}".format(ctx, command)
if len(operands) > 0:
userAction = '{0}\n\t\t"{1}"'.format(userAction, " ".join(operands))
logger.log(userAction)
#####################
# Commands
#####################
@client.command(name = "download")
async def download(ctx, *args:str):
""" Download to the host machine the first attachment provided by
the user. Said file should be given a destination path, but will
default if the name is not provided. If the destination is valid,
then it should start reading the destination into the file 16kb
at a time, at which point if an exception is not thrown it should
tell the user and the logger that the attachment has been
downloaded.
"""
attachment = ctx.message.attachments[0]
buffer = io.BytesIO()
await attachment.save(buffer)
try:
if len(args)>0:
filePath = args[0]
else:
filePath = attachment.filename
logger.log("Filepath defined: {0}".format(filePath))
with open(filePath, "wb") as f:
bufferSize=16384
while True:
buf = buffer.read(bufferSize)
if not buf:
break
f.write(buf)
logger.log("File Downloaded: {0}".format(filePath))
msg = "Attachment Downloaded"
except Exception as e:
errorMessage = str(e)
logger.log("Download Failed - {}".format(errorMessage))
msg = "Download Fail"
await ctx.send(msg)
@client.command(name = "upload")
async def upload(ctx, *args:str):
""" Upload from the host machine a file at the path specified by
the user through args. If successful, the machine should be able to
upload the file and log that it was successful. If there are
exceptions thrown, clarify to the user that the upload failed and
give the exception to the host machine in detail.
"""
try:
filePath = args[0]
logger.log("Filepath defined: {0}".format(filePath))
with open(filePath, "rb") as f:
await ctx.send(file=File(f, filePath))
logger.log("Upload successful: {0}".format(filePath))
except Exception as e:
errorMessage = str(e)
await ctx.send("Upload Fail")
logger.log("Upload Fail {0}".format(errorMessage))
@client.command(name = "bash")
async def upload(ctx, *args:str):
""" Receive a bash command from the discord chat and run in on the
appropriate bot instance.
"""
try:
# Turn the tuple of args into a single string
argsString = " ".join(args)
# Ensure nothing extremely dangerous was attempted
if "sudo" in argsString:
raise Exception("CANNOT EXECUTE SUPER USER COMMANDS")
# Run and Log the command
output = testTerminal.executeCommand(argsString)
logger.log("Command executed {0}".format(argsString))
# Check for empty output string
outStr = ("```{0}```".format(output["outputString"]) if
len(output["outputString"])>0 else "")
errStr = ("```{0}```".format(output["errorString"]) if
len(output["errorString"])>0 else "")
# Build message
msg = (
"Terminal: {0} \n".format(output["terminalName"])
+ "Current Dir: {0} \n".format(output["currentDirectory"])
+ "Output:{0}\n".format(outStr)
+ "Error:{0}\n".format(errStr)
)
logger.log(msg)
await ctx.send(msg)
except Exception as e:
# If an exception occured it should be logged and posted to
# discord
errorMessage = str(e)
msg = "Command Failed {0}".format(errorMessage)
logger.log(msg)
await ctx.send(msg)
@client.command(name = "cwd")
async def changeWorkingDirectory(ctx, *args:str):
""" Change the working directory of the terminal. """
try:
# The file path should be the first and only argument
filePath = args[0]
logger.log("Directory specified {0}".format(filePath))
# Set the currentDirectory of the terminal instance
testTerminal.currentDirectory = filePath
msg = "Terminal Directory set!"
logger.log(msg)
await ctx.send(msg)
except Exception as e:
errorMessage = str(e)
msg = "Changing Directory Failed {0}".format(errorMessage)
logger.log(msg)
await ctx.send(msg)
@client.command(name = "hello")
async def hello(ctx):
""" Clarifies that the command system is successfully implemented
and mentions the user while saying hello.
"""
msg = "Hello {0.author.mention}".format(ctx)
await ctx.send(msg)
@client.command(name = "shutdown")
async def shutdown(ctx):
""" Clarifies to the user that the bot is shutting down. Logs out.
Tells the host machine that the bot has logged out.
"""
await ctx.send("Shutting down...")
logger.log("{0.user.name} has logged out.".format(client))
await client.logout()
#####################
# Start the bot
#####################
client.run(TOKEN)