async def cmd(self, message): # Handle messages from commands author = message.author content = message.content[1::] # Remove cmd prefix character words = content.split(' ') word = words.pop(0).lower() # First word for cmd channel = message.channel try: await self.command_event("on_command", word, message=message, content=content, author=author, words=words, channel=channel) # await call(func, message, content, author, words, channel) # Invoke command except SystemExit as err: raise SystemExit(err.code) except Exception as e: try: await self.reply(message, "Unexpected error, please check the log :/") except Exception as ee: disp("Was unable to send error message to Discord...") raise ee from None disp(f"Unexpected error in cmd \"{word}\": ") raise e from None
async def announce(self, *_args, force_announce=False, **kwargs): client = kwargs["client"] if client.voice_client is None: return if "user_id" in kwargs.keys(): user_id = kwargs["user_id"] elif "member" in kwargs.keys(): # Play a user's announce sound user_id = kwargs["member"].id else: # Play our own announce sound user_id = client.user.id if not self.should_announce(client, user_id, force_announce=force_announce): return str_id = str(user_id) announce_dir = self.cfg["announce_dir"] # Get all files in directory files = [file for file in os.listdir(announce_dir) if os.path.isfile(os.path.join(announce_dir, file))] path = None for file in files: if str_id + '.' in file: path = os.path.join(announce_dir, file) break if path is None: # TODO Add default announce sound functionality (Fakas) disp(f"No announce sound for ID {str_id}") else: self.announced[user_id] = time.time() client.play(path)
async def on_ready(self): # When logged in report = f"Logged in as {self.user.name}! ({self.user.id})" disp(report) self.event_loop = asyncio.get_event_loop() await self.async_module_event(get_function_name())
def make_config_dir(): """ Create a configuration directory if it doesn't exist :return: Absolute path to configuration directory """ config_dir = path.join(getcwd(), "config") if not path.exists(config_dir): disp("No config directory exists, it will be created!") makedirs(config_dir) return config_dir
def get_json(name: str, default: Union[list, dict]): json_path = get_config_path(name) if not path.exists(json_path): disp(f"Generating JSON file for module \"{name}\"...") set_json(name, default) file = open(json_path, 'r') content = json.load(file) file.close() return content
def load_modules(self): everything = mb_modules.__dict__ for key in everything.keys(): if key[0] != '_' and ismodule(everything[key]): mb_module = everything[key] if hasattr(mb_module, "mb_mods"): for Mod in mb_module.mb_mods: if is_module(Mod) and verify_module(Mod): mod = Mod() disp(f"Loading module \"{mod.mb_name}\"...") self.modules.update({key: mod})
async def setup_connection(self): try: await self.msg_client.connect() except ConnectionError: self.conn_failed = True disp(self.conn_failed_text) return False await self.msg_client.register_channel() await self.msg_client.register_queue(self.cfg["radio_queue"]) return True
async def on_error(self, event, *args, **kwargs): try: await super(Client, self).on_error(event, *args, **kwargs) except Exception: disp("Default error handling failed! Traceback:") traceback.print_exc() # noinspection PyBroadException try: await self.async_module_event(get_function_name(), *args, **kwargs) except Exception: disp("Encountered an error in an on_error function! Traceback:") traceback.print_exc()
def play(self, audio): if self.voice_client is not None: self.stop() if type(audio) is str: audio = discord.FFmpegPCMAudio(audio) audio = discord.PCMVolumeTransformer(audio, volume=self.cfg["audio_volume"]) if self.voice_client.is_playing() or self.voice_client.is_paused(): # We check twice to avoid some race conditions self.voice_client.stop() self.voice_client.play(audio) else: disp("Tried to play audio but was not connected to a voice channel!")
async def delete(self, obj): # Delete something (probably a Discord object) if type(obj) is discord.Message: try: await obj.delete() return True except discord.Forbidden: text = "I don't have permissions to delete messages :/" disp("Error: " + text) traceback.print_exc() await self.reply(obj, text) except Exception as e: disp("Unexpected error while deleting a message:") raise e from None return False
async def reply_callback(self, message: aio_pika.IncomingMessage): async with message.process(): msg = message.body.decode("utf-8") content = json.loads(msg) if "context" not in content: disp("Error: Radio response did not include a context value!") return if "reply" in content: text = content["reply"] chan_id, msg_id = content["context"] else: text = "Radio service returned an invalid response! :/" d_chan = await self.client.fetch_channel(chan_id) d_msg = await d_chan.fetch_message(msg_id) await self.client.reply(d_msg, text)
async def announce(*_args, force_announce=False, **kwargs): client = kwargs["client"] if client.vclient is None: return if "user_id" in kwargs.keys(): user_id = kwargs["user_id"] elif "member" in kwargs.keys(): # Play a user's announce sound user_id = kwargs["member"].id else: # Play our own announce sound user_id = client.user.id do_announce = False if force_announce: do_announce = True else: for member in client.vclient.channel.members: if member.id == user_id: do_announce = True break if not do_announce: return user_id = str(user_id) announce_dir = client.cfg["announce_dir"] # Get all files in directory files = [ file for file in os.listdir(announce_dir) if os.path.isfile(os.path.join(announce_dir, file)) ] path = None for file in files: if user_id + '.' in file: path = os.path.join(announce_dir, file) break if path is None: # TODO Add default announce sound functionality disp(f"No announce sound for ID {user_id}") else: client.play(path)
async def shutdown(self, message, *_args): disp("Received shutdown command!") msg = self.cfg["shutdown_message"] await self.reply(message, msg) disp("Logging out from Discord...") await self.logout() disp("Goodbye!")
def get_config(target): if target is str: name = target else: file_path = frame_util.get_class_file(target) name = path.split(file_path) name = path.splitext(name[1]) name = name[0] config_dir = path.join(getcwd(), "config") if not path.exists(config_dir): disp("No config directory exists, it will be created!") makedirs(config_dir) config_path = path.join(config_dir, f"{name}.json") if not path.exists(config_path): disp(f"Generating config file for module \"{name}\"...") config_file = open(config_path, 'w') json.dump(target.mb_default_config, config_file, indent=4) config_file.close() config_file = open(config_path, 'r') config = json.load(config_file) config_file.close() return config
async def announce_cmd(self, *args, **kwargs): # Announce the user's or an arbitrary ID author = kwargs["author"] words = kwargs["words"] if len(words) > 0: target = words[0] if "<@!" in target: # Camper has a weird @ID target = target[3:-1] if "<@" in target: target = target[2:-1] try: user_id = int(target) except ValueError: disp(f"{target} is not a valid integer and cannot be announced") return else: user_id = author.id if user_id is not None: kwargs["user_id"] = user_id await self.announce(*args, **kwargs, force_announce=True)
def init(*_args, **_kwargs): disp("Logging module enabled!")
def print_msg(*_args, **kwargs): message = kwargs["message"] # Print a message to the console msg = f"{message.author}: {message.content}" disp(msg)
def load_module(self, mod, key): if is_module(mod) and verify_module(mod): mod = mod() disp(f"Loading module \"{mod.mb_name}\"...") self.modules.update({key: mod})