def help(client, topic='', *, H=False, h=False, domain=''): """ The command for receiving in-game help. Searches through commands, subscribers, and active_services and returns either a list of available matching helpfiles, or the docstring of the single match. :param client: The client issuing the help command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. :param topic: None by default, otherwise the name of the command/service/subscriber you're looking to get help with. :param domain: Optionally limit where you're looking for help to either cmds, subscribers, or services. For example, you might enter ``help talker`` to learn more about qtMUD's talker system. You would be prompted to enter either ``help --domain=cmds talker`` to learn about the in-game talker command or ``help --domain=services talker`` to learn about the Talker service`. """ output = '' brief = ('help [-Hh] [--domain=$domain] [topic]\n\n' 'Search for help for *topic*. Use *--domain=* to limit where you ' 'search to cmds, subscribers, or services.') matches = [] help_locations = { 'cmds': [client.commands], 'subscribers': [qtmud.subscribers], 'services': [qtmud.active_services] } if H: output += help.__doc__ elif h: output += brief elif topic: topic = topic.lower() for _domain, locations in help_locations.items(): if domain == _domain or not domain: for location in locations: if topic in location: # TODO Warning Expected type 'Union[Integral, # slice]', got 'str' instead matches.append(location[topic]) if matches: if len(matches) == 1: output += matches[0].__doc__ else: output += ( 'Multiple matches found, try "help ' '--domain=*domain* {}" where *domain* is one of : {}' ''.format( topic, ', '.join( m.__module__.split('.')[-1] for m in matches))) else: output += help.__doc__ if not output: output = 'No help found. Try looking at https://qtmud.rtfd.io' qtmud.schedule('send', recipient=client, text=output) return output
def options(client, value, *, H=False, h=False, attr=None): """ Command to set client options. :param client: The client issuing the command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. """ output = '' brief = ('syntax: options [-Hh]\n\n' 'Sets options for the client. `This command is a work in ' 'progress.`') if H: output += options.__doc__ elif h: output += brief elif attr: try: setattr(client, attr, value) output += 'You set your `{attr}` to `{value}`.'.format(**locals()) except AttributeError as err: output += 'You don\'t have a `{attr}` attribute.'.format( **locals()) qtmud.log.warning('{client.name} failed to set {attr}: {err}' ''.format(**locals()), exc_info=True) else: output += ( 'This command is for setting options about your player ' 'account. Since it isn\'t really intended for you to use if ' 'you don\'t know what you\'re doing, I\'m not going to ' 'explain it further.') qtmud.schedule('send', recipiennt=client, text=output)
def foo(client, *, H=False, h=False, p=False): """ The dedicated test command :param client: The client issuing the foo command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. This is a testing command, so you should check the source itself for information on what it actually does. """ output = '' brief = ('syntax: foo [-Hh]\n\n' 'Check source for real information, this is a testing command.') if H: output += foo.__doc__ elif h: output = brief elif p: line = ('You can also use %^RED%^nesting %^GREEN%^Pinkfish-style ' '%^B_YELLOW%^markup tags%^, though they%^ become less%^ ' 'readable.') output += ('You test the pinkfish_parser with the following line:\n\n' '{}\n\nIt returns: {}'.format(line, qtmud.pinkfish_parse(line))) else: output = 'You foo, to no effect. (`this is a development command`)' if output: qtmud.schedule('send', recipient=client, text=output) return True
def broadcast(self, channel, speaker, message): for listener in self.channels[channel]: qtmud.schedule('send', recipient=listener, text='`(`{}`)` {}: {}'.format( channel, speaker, message)) self.history[channel].append('{}: {}'.format(speaker, message))
def tune_channel(self, client, channel): if channel not in self.channels: self.new_channel(channel) self.channels[channel].append(client) client.channels.append(channel) qtmud.schedule('send', recipient=client, text='You tune into the {} channel'.format(channel))
def talker(client, channel=None, *, H=False, h=False, l=False, t=False, d=False): """ Command for interacting with the Talker service. :param client: The client issuing the talker command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. :param channel: The channel you're getting information on. :param l: Show the channel's history. :param t: Tune into a channel :param d: Drop a channel .. todo:: --depth argument to specify how much history to show """ output = '' brief = ('syntax: talker [-Hhltd] [channel]\n\n' 'If entered without argument, lists the channels you\'re tuned ' 'into. If a channel is given, shows information about that ' 'channel. If the -l flag is given, shows that channel\'s logs.') talker_service = qtmud.active_services['talker'] if H: output += talker.__doc__ elif h: output = brief else: if not channel: output += ('you\'re listening to {}' ''.format([c for c in client.channels])) else: if d: for _channel in channel.split(','): talker_service.drop_channel(client, _channel) elif t: for _channel in channel.split(','): talker_service.tune_channel(client=client, channel=_channel) elif l: output += ('({}) channel log:\n{}' ''.format( channel, '\n'.join( m for m in qtmud.active_services['talker']. history[channel]))) else: # TODO output += talker_service.summarize(channel) output += ('This will show you output about that channel ' 'and its listeners, in the future.') if output: qtmud.schedule('send', recipient=client, text=output) return True
def tick(self): read, write, error = select.select(self.connections, [ conn for conn, client in self.clients.items() if client.send_buffer != '' ], [], 0) if read: for conn in read: if conn is self.ip4_socket or conn is self.ip6_socket: new_conn, addr = conn.accept() qtmud.log.debug('new connection accepted from %s', format(addr)) client = qtmud.Client() client.update({ 'addr': addr, 'send_buffer': '', 'recv_buffer': '' }) self.connections.append(new_conn) self.clients[new_conn] = client client.input_parser = 'client_login_parser' qtmud.schedule('send', recipient=client, text=qtmud.SPLASH) else: try: data = conn.recv(1024) except ConnectionResetError as err: qtmud.log.debug('lost connection from: {}:\n' '{}'.format(self.clients[conn].addr, err), exc_info=True) qtmud.schedule('client_disconnect', client=self.clients[conn]) return if data == b'': qtmud.log.debug('disconnected from %s', format(self.clients[conn].addr)) qtmud.schedule('client_disconnect', client=self.clients[conn]) else: client = self.clients[conn] client.recv_buffer += data.decode('utf8', 'ignore') if '\n' in client.recv_buffer: split = client.recv_buffer.rstrip().split('\n', 1) if len(split) == 2: line, client.recv_buffer = split else: line, client.recv_buffer = split[0], '' qtmud.schedule('send', recipient=client, text='> {}'.format(line)) qtmud.schedule('client_input_parser', client=client, line=line) if write: for conn in write: conn.send(self.clients[conn].send_buffer.encode('utf8')) self.clients[conn].send_buffer = ''
def whatami(client, *, H=False, h=False): output = '' brief = ('syntax: whatami [-Hh]\n\n' 'Shows what sort of object you are.') if H: output += who.__doc__ elif h: output = brief else: output += ('{}'.format(client.__class__.__name__)) qtmud.schedule('send', recipient=client, text=output)
def broadcast(channel, speaker, message): """ Send a message from speaker to everyone on the channel. """ if not message: qtmud.schedule('send', recipient=speaker, text='syntax: {0} <message>\n' 'Sends `message` to everyone tuned into `{0}`' ''.format(channel)) else: qtmud.active_services['talker'].broadcast(channel=channel, speaker=speaker.name, message=message) return True
def whoami(client, *, H=False, h=False): """ Command to quit qtMUD :param client: The client issuing the whoami command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. """ output = '' brief = ('syntax: whoami [-Hh]\n\n' 'Shows your own name.') if H: output += whoami.__doc__ elif h: output = brief else: output = ('You are {}'.format(client.name)) qtmud.schedule('send', recipient=client, text=output) return True
def client_disconnect(client): """ Handle removing a client from qtMUD .. warning:: This is likely buggy and going to change """ mudsocket = qtmud.active_services['mudsocket'] qtmud.log.debug('disconnecting %s from qtmud.', client.name) for other in qtmud.connected_clients: qtmud.schedule('send', recipient=other, text='{} disconnected.'.format(client.name)) try: qtmud.connected_clients.remove(client) except ValueError: pass socket = mudsocket.get_socket_by_thing(client) if socket: mudsocket.clients.pop(socket) mudsocket.connections.remove(socket) return True
def who(client, *, H=False, h=False): """ Command to check your name :param client: The client issuing the who command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. """ output = '' brief = ('syntax: who [-Hh]\n\n' 'Shows a list of the currently connected clients.') if H: output += who.__doc__ elif h: output = brief else: output = ('The following clients are currently connected:\n' '{}'.format('\n'.join( [c.name for c in qtmud.connected_clients]))) qtmud.schedule('send', recipient=client, text=output) return True
def tell(client, *payload, H=False, h=False): """ Command for private messaging other players. :param client: The client issuing the command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. """ output = '' brief = ('syntax: tell [-Hh] <recipient> <message>\n\n' 'Sends `message` to `recipient`.') if H: output += tell.__doc__ elif h: output += brief elif payload: payload = [w for w in payload] if len(payload) > 1: recipients = qtmud.search_connected_clients_by_name(payload.pop(0)) if len(recipients) == 1: recipient = recipients[0] message = ' '.join(payload) if client == recipient: output += 'You tell `yourself`: {}'.format(message) else: output += 'You tell `{}`: {}'.format( recipient.name, message) qtmud.schedule('send', recipient=recipient, text='`{}` tells you: {}'.format( client.name, message)) elif len(recipients) > 1: output += ('More than one match: {}' ''.format(', '.join([r.name for r in recipients]))) else: output += 'Couldn\'t find that client.' if not output: output += brief qtmud.schedule('send', recipient=client, text=output)
def shutdown(): """ Handles qtMUD shutting down. """ qtmud.log.debug('shutdown() occurring') for client in qtmud.connected_clients: qtmud.schedule('client_disconnect', client=client) while True: if qtmud.events: qtmud.log.debug('processing final events %s', qtmud.events) qtmud.tick() else: break for service in qtmud.active_services: service = qtmud.active_services[service] qtmud.log.debug('shutdown()ing %s', service.__class__.__name__) try: service.shutdown() qtmud.log.debug('shutdown() %s successfully', service.__class__.__name__) except Exception as err: qtmud.log.warning('%s failed to shutdown: %s', service.__class__.__name__, err) qtmud.log.info('shutdown() finished, raising SystemExit') raise SystemExit
def commands(client, *, H=False, h=False): """ Sends a list of the client's commands to the client. :param client: The client issuing the command command. (That'd be you.) This isn't part of the command you enter. :param H: Shows the client this docstring. :param h: Shows the client a brief help. Creates a ``send`` event to tell the client a list of their commands. """ output = '' brief = ('commands [-Hh]\n\n' 'If entered without argument, lists all your valid commands.') if H: output += commands.__doc__ elif h: output += brief else: output += ('Your commands are: {}' ''.format(', '.join([c for c in client.commands.keys()]))) if output: qtmud.schedule('send', recipient=client, text=output) return True
def client_command_parser(client, line): """ Once a client has logged in, this method handles parsing their input. """ if line: spl = line.split(' ') command = spl[0] if command in client.commands: if len(spl) > 1: targs = spl[1:] else: targs = [] kwargs = {} args = [] for arg in targs: if arg.startswith('--'): if '=' in arg: targ = arg[2:].split('=', 1) if len(targ) == 2: kwargs[targ[0]] = targ[1] elif arg.startswith('-'): for char in arg[1:]: kwargs[char] = True else: args.append(arg) try: client.commands[command](*args, **kwargs) except (SyntaxWarning, SyntaxError, TypeError) as err: qtmud.schedule('send', recipient=client, text='{} command failed: {}' ''.format(command, err)) client.commands[command](h=True) qtmud.log.warning('%s\'s %s command failed: %s', client.name, command, err, exc_info=True) elif command in client.channels: message = ' '.join(spl[1:]) qtmud.schedule('broadcast', channel=command, speaker=client, message=message) else: qtmud.schedule('send', recipient=client, text=('{} is not a valid command; check ' '"commands" for your commands.' ''.format(command))) return True
def quit(client, *, H=False, h=False): """ Command to quit qtMUD :param client: The client issuing the quit command. (That'd be you.) **This isn't part of the command you enter.** :param H: Shows the client this docstring. :param h: Shows the client a brief help. """ output = '' brief = ('syntax: help [-Hh]\n\n' 'Causes the client to leave the game.') if H: output += quit.__doc__ elif h: output = brief else: qtmud.schedule('send', recipient=client, text='you quit goodbye') qtmud.schedule('client_disconnect', client=client) if output: qtmud.schedule('send', recipient=client, text=output) return True
def client_login_parser(client, line): """ Handle logs-in for arriving players - right now, just a basic check against qtmud.client_accounts to see if the client is there already. """ output = '' ##### # # start login process # ##### if not hasattr(client, 'login_stage'): client.login_stage = 0 output = 'Input [desired] client name and press <enter>.' ##### # # check if client exits # ##### elif client.login_stage == 0: if line in qtmud.client_accounts.keys(): output = ('There\'s a client named {}, if you\'re them, type your ' 'password and press <enter>'.format(line)) client.login_stage = 2 elif line: output = ('No client named {}, going to make an account with that ' 'name. Type your desired password and press <enter>.' ''.format(line)) client.login_stage = 1 else: output = ('Your client name can\'t be blank. Input what name ' 'you\'d like to use and press <enter>.') client.name = line ##### # # register new client # ##### elif client.login_stage == 1: qtmud.client_accounts[client.name] = {'password': line} qtmud.save_client_accounts() client.login_stage = 9 output = ('Client account registered with name {}, press ' '<enter> to finish logging in.'.format(client.name)) ##### # # login existing account # ##### elif client.login_stage == 2: if line == qtmud.client_accounts[client.name]['password']: client.login_stage = 9 output = ('That\'s the correct password, press <enter> to finish ' 'logging in.') else: client.login_stage = 0 output = ('That\'s not the right password for that account - ' 'type your [desired] client name and press <enter>.') elif client.login_stage == 9: if qtmud.MUDLIB: client.input_parser = 'client_mudlib_login_parser' else: client.input_parser = 'client_command_parser' qtmud.active_services['talker'].tune_channel(client=client, channel='one') qtmud.connected_clients.append(client) for c in qtmud.connected_clients: qtmud.schedule('send', recipient=c, text='`{}` has connected'.format(client.name)) if output: qtmud.schedule('send', recipient=client, text=output) return True
def client_input_parser(client, line): """ Pushes a client's input to their designated parser subscription. """ qtmud.schedule('{}'.format(client.input_parser), client=client, line=line) return True