-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcommand_line.py
More file actions
178 lines (152 loc) · 6.26 KB
/
command_line.py
File metadata and controls
178 lines (152 loc) · 6.26 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import asyncio
import cmd
import os
import signal
from threading import Thread
import logging
from utils import settings
__author__ = "reggna"
# CLI to control the bots functionality.
#
# Once created, call command_line.start() in order to create the seprate thread to run the CLI in.
# On teardown, call command_line.wait_until_done() to join the thread back and make a clean exit.
class CommandLine(cmd.Cmd):
def __init__(self, bot):
super().__init__()
self.bot = bot
self.intro = "Welcome to platimumshrimp CLI. Type ? or help to list available commands."
self.prompt = "PlatinumShrimp >> "
def do_exit(self, _):
"""Exit the bot."""
self.bot.loop.stop()
return True
def do_list_servers(self, _):
"""List connected servers with status."""
for name, server in self.bot.servers.items():
status = "Connected" if server.is_connected() else "Disconnected"
print(f"{name}: {status} | Host: {server.server}:{server.port}")
def do_list_channels(self, arg):
"""List channels on a given server. Usage: list_channels <server>"""
name = arg.strip()
if name not in self.bot.servers:
print(f"Server '{name}' not found.")
return
server = self.bot.servers[name]
channels = getattr(server, "channels", set())
if not channels:
print(f"No channels joined on server '{name}'.")
return
for channel in sorted(channels):
print(f"- {channel}")
def do_join_channel(self, arg):
"""Join a channel. Usage: join_channel <server> <channel>"""
args = arg.split(" ")
if len(args) != 2:
print("Usage: join_channel <server> <channel>")
return
server_name, channel = args
server = self.bot.servers.get(server_name)
if server:
server.join(channel)
else:
print(f"Server '{server_name}' not found.")
def do_part_channel(self, arg):
"""Exit (part) a channel. Usage: part_channel <server> <channel>"""
args = arg.split(" ")
if len(args) != 2:
print("Usage: part_channel <server> <channel>")
return
server_name, channel = args
server = self.bot.servers.get(server_name)
if server:
server.part(channel)
else:
print(f"Server '{server_name}' not found.")
def do_send_message(self, arg):
"""Send a message. Usage: send_message <server> <channel> <message>"""
args = arg.split(" ")
if len(args) < 3:
print("Usage: send_message <server> <channel> <message>")
return
server_name, channel, message = args[0], args[1], " ".join(args[2:])
server = self.bot.servers.get(server_name)
if server:
server.privmsg(channel, message)
else:
print(f"Server '{server_name}' not found.")
def do_reload_settings(self, _):
"""Reload the settings file."""
try:
self.bot.settings = settings.load_settings()
print("Settings reloaded.")
except Exception as e:
print(f"Failed to reload settings: {e}")
def do_list_plugins(self, _):
"""List currently loaded plugins."""
if not self.bot.plugins:
print("No plugins loaded.")
return
for plugin in self.bot.plugins:
print(f"{plugin.name} (PID: {plugin.pid})")
def do_load_plugin(self, arg):
"""Load a plugin. Usage: load_plugin <plugin_name>"""
name = arg.strip()
if not name:
print("Usage: load_plugin <plugin_name>")
return
plugin_settings = self.bot.settings.get("plugins", {}).get(name, {})
async def _load():
self.bot.load_plugin(name, plugin_settings)
print(f"Plugin '{name}' loading initiated with settings {plugin_settings}.")
# Schedule _load on the bot's main asyncio loop
future = asyncio.run_coroutine_threadsafe(_load(), self.bot.loop)
try:
future.result()
except Exception as e:
print(f"Error loading plugin '{name}': {e}")
def do_reload_plugin(self, arg):
"""Reload a plugin. Usage: reload_plugin <plugin_name>"""
name = arg.strip()
self.do_unload_plugin(name)
self.do_reload_settings(arg)
self.do_load_plugin(name)
def do_unload_plugin(self, arg):
"""Unload a plugin. Usage: unload_plugin <plugin_name>"""
name = arg.strip()
plugin = next((p for p in self.bot.plugins if p.name == name), None)
if not plugin:
print(f"Plugin '{name}' not found.")
return
try:
os.kill(plugin.pid, signal.SIGTERM)
self.bot.plugins.remove(plugin)
print(f"Plugin '{name}' unloaded.")
except Exception as e:
print(f"Error unloading plugin '{name}': {e}")
# A lot of commands share auto complete so let's override the default instead of individuals
def completedefault(self, text, line, start_index, end_index):
args = line.split(" ")
if args[0] in ["unload_plugin", "reload_plugin"] and self.bot.plugins:
return [plugin.name for plugin in self.bot.plugins if plugin.name.startswith(text)]
# Exit early for commands with no auto-complete
if not args[0] in ["send_message", "list_channels", "part_channel", "join_channel"]:
return
if len(args) < 3: # Complete server names
return [s + " " for s in self.bot.servers if s.startswith(text)]
elif len(args) == 3: # Complete channel names
if args[1] not in self.bot.servers:
return
server = self.bot.servers[args[1]]
return [c + " " for c in getattr(server, "channels", set()) if c.startswith(args[2])]
# Override the emptyline function to avoid the default of repeating the last command
def emptyline(self):
pass
# Let's exit the bot on EOF (aka ctrl+d)
def do_EOF(self, _):
print("") # Just to make a new line
return self.do_exit(_)
def start(self):
self.thread = Thread(target=self.cmdloop)
self.thread.start()
def wait_until_done(self):
self.thread.join()