def add_poll(self, value): test = time.strftime('%y-%m-%d %H%M-%S') if not ([item for item in self.poll_list if item[0] == value]): if len(self.poll_list) < self.server.config['poll_slots']: self.poll_list.append([value, test]) tmp = time.strftime('%y-%m-%d %H:%M:%S') newfile = { 'name': value, 'polldetail': None, 'multivote': False, 'choices': ["Yes", "No"], 'votes': {"yes": 0, "no": 0}, 'created': tmp, 'log': [], } with open('storage/poll/{} \'{}\'.yaml'.format(test, value), 'w') as file: yaml.dump(newfile, file, default_flow_style=False) logger.log_serverpoll('Poll \'{}\' added successfully.'.format(value)) else: logger.log_serverpoll('Failed to add poll. Reason: The poll queue is full.') raise ServerError('The Poll Queue is full!') else: logger.log_serverpoll('Failed to add poll. Reason: This poll already exists.') raise ServerError('This poll already exists.') self.write_poll_list()
def load_config(self): with Constants.fopen('config/config.yaml', 'r', encoding='utf-8') as cfg: self.config = Constants.yaml_load(cfg) self.config['motd'] = self.config['motd'].replace('\\n', ' \n') for i in range(1, 8): daily_gmpass = '******'.format(i) if daily_gmpass not in self.config or not self.config[daily_gmpass]: self.config[daily_gmpass] = None # Default values to fill in config.yaml if not present defaults_for_tags = { 'discord_link': None, 'max_numdice': 20, 'max_numfaces': 11037, 'max_modifier_length': 12, 'max_acceptable_term': 22074, 'def_numdice': 1, 'def_numfaces': 6, 'def_modifier': '', 'blackout_background': 'Blackout_HD', 'default_area_description': 'No description.', 'party_lights_timeout': 10, 'showname_max_length': 30, 'sneak_handicap': 5, 'spectator_name': 'SPECTATOR', 'music_change_floodguard': { 'times_per_interval': 1, 'interval_length': 0, 'mute_length': 0 } } for (tag, value) in defaults_for_tags.items(): if tag not in self.config: self.config[tag] = value # Check that all passwords were generated and that they are unique passwords = [ 'guardpass', 'modpass', 'cmpass', 'gmpass', 'gmpass1', 'gmpass2', 'gmpass3', 'gmpass4', 'gmpass5', 'gmpass6', 'gmpass7' ] for password_type in passwords: if password_type not in self.config: info = ( 'Password "{}" not defined in server/config.yaml. Please make sure it is ' 'set and try again.'.format(password_type)) raise ServerError(info) for (i, password1) in enumerate(passwords): for (j, password2) in enumerate(passwords): if i != j and self.config[password1] == self.config[ password2] != None: info = ( 'Passwords "{}" and "{}" in server/config.yaml match. ' 'Please change them so they are different and try again.' .format(password1, password2)) raise ServerError(info)
def fopen(file, *args, **kwargs): try: f = open(file, *args, **kwargs) return f except FileNotFoundError: info = 'File not found: {}'.format(file) raise ServerError(info, code="FileNotFound") except OSError as ex: raise ServerError(str(ex))
async def as_afk_kick(self, client, args): afk_delay, afk_sendto = args try: delay = int(afk_delay)*60 # afk_delay is in minutes, so convert to seconds except (TypeError, ValueError): info = ('The area file contains an invalid AFK kick delay for area {}: {}'. format(client.area.id, afk_delay)) raise ServerError(info) if delay <= 0: # Assumes 0-minute delay means that AFK kicking is disabled return try: await asyncio.sleep(delay) except asyncio.CancelledError: raise else: try: area = client.server.area_manager.get_area_by_id(int(afk_sendto)) except Exception: info = ('The area file contains an invalid AFK kick destination area for area {}: ' '{}'.format(client.area.id, afk_sendto)) raise ServerError(info) if client.area.id == afk_sendto: # Don't try and kick back to same area return if client.char_id < 0: # Assumes spectators are exempted from AFK kicks return if client.is_staff(): # Assumes staff are exempted from AFK kicks return try: original_area = client.area original_name = client.displayname client.change_area(area, override_passages=True, override_effects=True, ignore_bleeding=True) except Exception: pass # Server raised an error trying to perform the AFK kick, ignore AFK kick else: client.send_ooc('You were kicked from area {} to area {} for being inactive for ' '{} minutes.'.format(original_area.id, afk_sendto, afk_delay)) if client.area.is_locked or client.area.is_modlocked: try: # Try and remove the IPID from the area's invite list client.area.invite_list.pop(client.ipid) except KeyError: pass # Would only happen if they joined the locked area through mod powers if client.party: p = client.party client.party.remove_member(client) client.send_ooc('You were also kicked off from your party.') for c in p.get_members(): c.send_ooc('{} was AFK kicked from your party.'.format(original_name))
def remove_ban(self, ip): try: try: int(ip) except ValueError: ipaddress.ip_address(ip) ip = self.server.get_ipid(ip) except ValueError: raise ServerError('Argument must be an IP address or IPID.') if ip in self.bans: self.bans.remove(ip) else: raise ServerError('User is already not banned.') self.write_banlist()
async def as_afk_kick(self, client, args): afk_delay, afk_sendto = args try: delay = int( afk_delay ) * 60 # afk_delay is in minutes, so convert to seconds except (TypeError, ValueError): raise ServerError( 'The area file contains an invalid AFK kick delay for area {}: {}' .format(client.area.id, afk_delay)) if delay <= 0: # Assumes 0-minute delay means that AFK kicking is disabled return try: await asyncio.sleep(delay) except asyncio.CancelledError: raise else: try: area = client.server.area_manager.get_area_by_id( int(afk_sendto)) except: raise ServerError( 'The area file contains an invalid AFK kick destination area for area {}: {}' .format(client.area.id, afk_sendto)) if client.area.id == afk_sendto: # Don't try and kick back to same area return if client.char_id < 0: # Assumes spectators are exempted from AFK kicks return if client.is_staff(): # Assumes staff are exempted from AFK kicks return try: original_area = client.area client.change_area(area, override_passages=True, override_effects=True, ignore_bleeding=True) except: pass # Server raised an error trying to perform the AFK kick, ignore AFK kick else: client.send_host_message( "You were kicked from area {} to area {} for being inactive for {} minutes." .format(original_area.id, afk_sendto, afk_delay)) if client.area.is_locked or client.area.is_modlocked: client.area.invite_list.pop(client.ipid)
def remove_ban(self, ip): try: try: int(ip) except ValueError: ipaddress.ip_address(ip) ip = self.server.get_ipid(ip) except ValueError: raise ServerError( 'Argument must be an IP address or 10-digit number.') if ip in self.bans: self.bans.remove(ip) else: raise ServerError('This IPID is not banned.') self.write_banlist()
def get_votelist(self, value): try: if [i for i in self.poll_list if i[0] == "{}".format(value)]: poll_selected = [i[1] for i in self.poll_list if i[0] == "{}".format(value)] output = ('{} \'{}\''.format("".join(poll_selected), value)) stream = open('storage/poll/{}.yaml'.format(output), 'r') stream2 = yaml.load(stream) log = stream2['log'] return log else: return None except FileNotFoundError: raise ServerError('The specified poll has no file associated with it.') except IndexError: raise ServerError('The poll list is currently empty.')
def add_ban(self, ip): try: try: int(ip) except ValueError: ipaddress.ip_address(ip) ip = self.server.get_ipid(ip) except ValueError: raise ServerError( 'Argument must be an IP address or 10-digit number.') if ip not in self.bans: self.bans.append(ip) else: raise ServerError('User is already banned.') self.write_banlist()
def ooc_cmd_playrandom(client, arg): """ Plays a random track. Usage: /playrandom """ if len(arg) > 0: raise ArgumentError('This command takes no arguments.') index = 0 for item in client.server.music_list: for song in item['songs']: index += 1 if index == 0: raise ServerError( 'No music found.') else: music_set = set(range(index)) trackid = random.choice(tuple(music_set)) index = 1 for item in client.server.music_list: for song in item['songs']: if index == trackid: client.area.play_music(song['name'], client.char_id, song['length']) client.area.add_music_playing(client, song['name']) database.log_room('play', client, client.area, message=song['name']) return else: index += 1
def add_poll_choice(self, client, value, add): try: if [i for i in self.poll_list if i[0] == "{}".format(value)]: poll_selected = [ i[1] for i in self.poll_list if i[0] == "{}".format(value) ] output = ('{} \'{}\''.format("".join(poll_selected), value)) stream = open('storage/poll/{}.yaml'.format(output), 'r') stream2 = yaml.load(stream) if add.lower() in [x.lower() for x in stream2['choices']]: client.send_host_message('Item already a choice.') return stream2['choices'].append(str(add)) stream2['votes'][add.lower()] = 0 with open('storage/poll/{}.yaml'.format(output), 'w') as votelist_file: yaml.dump(stream2, votelist_file, default_flow_style=False) return stream2['choices'] else: return None except FileNotFoundError: raise ServerError( 'The specified poll has no file associated with it.') except IndexError: return
def remove_poll_choice(self, client, value, remove): try: if [i for i in self.poll_list if i[0] == "{}".format(value)]: poll_selected = [ i[1] for i in self.poll_list if i[0] == "{}".format(value) ] output = ('{} \'{}\''.format("".join(poll_selected), value)) stream = open('storage/poll/{}.yaml'.format(output), 'r') stream2 = yaml.load(stream) choices = stream2['choices'] if not remove in choices: client.send_host_message('Item is not a choice.') return stream2['choices'] = [x for x in choices if not x == remove] stream2['votes'].pop(remove.lower()) with open('storage/poll/{}.yaml'.format(output), 'w') as votelist_file: yaml.dump(stream2, votelist_file, default_flow_style=False) return stream2['choices'] else: return None except FileNotFoundError: raise ServerError( 'The specified poll has no file associated with it.') except IndexError: return
def get_char_id_by_name(self, name): if name == self.config['spectator_name']: return -1 for i, ch in enumerate(self.char_list): if ch.lower() == name.lower(): return i raise ServerError('Character not found.')
def get_song_data(self, music, area): """ Get information about a track, if exists. :param music: track name :returns: tuple (name, length or -1) :raises: ServerError if track not found """ for item in self.music_list: if item['category'] == music: return '~stop.mp3', 0, -1, False for song in item['songs']: if song['name'] == music: try: return song['name'], song['length'], song['mod'] except KeyError: try: return song['name'], song['length'], -1, False except KeyError: return song['name'], 0, -1, False if len(area.cmusic_list) != 0: for item in area.cmusic_list: if item['category'] == music: return '~stop.mp3', 0, -1, True if len(item['songs']) != 0: for song in item['songs']: if song['name'] == music: try: return song['name'], song['length'], song[ 'mod'], True except KeyError: return song['name'], song['length'], -1, True raise ServerError('Music not found.')
def add_ban(self, ipid, reason): ipid = str(ipid) if not self.is_banned(ipid): self.bans[ipid] = {'Reason': reason} else: raise ServerError('This IPID is already banned.') self.write_banlist()
def remove_ban(self, ipid): ipid = str(ipid) if self.is_banned(ipid): del self.bans[ipid] else: raise ServerError('This IPID is not banned.') self.write_banlist()
def add_ban(self, ip): try: x = len(ip) except AttributeError: raise ServerError('Argument must be an 12-digit number.') if x == 12: self.bans[ip] = True self.write_banlist()
def rolla_reload(area): try: import yaml with open('config/dice.yaml', 'r') as dice: area.ability_dice = yaml.safe_load(dice) except: raise ServerError( 'There was an error parsing the ability dice configuration. Check your syntax.' )
def get_song_data(self, music): for item in self.music_list: if item['category'] == music: return item['category'], -1 for song in item['songs']: if song['name'] == music: try: return song['name'], song['length'] except KeyError: return song['name'], -1 raise ServerError('Music not found.')
def remove_poll(self, value): if ([i for i in self.poll_list if i[0] == "{}".format(value)]): self.poll_list = [i for i in self.poll_list if i[0] != "{}".format(value)] logger.log_serverpoll('Poll \'{}\' removed.'.format(value)) elif value == "all": self.poll_list = [] logger.log_serverpoll('All polls removed.') else: logger.log_serverpoll('Poll removal failed. Reason: The specified poll does not exist.') raise ServerError('The specified poll does not exist.') self.write_poll_list()
def get_char_id_by_name(self, name): """ Get a character ID by the name of the character. :param name: name of character :returns: Character ID """ for i, ch in enumerate(self.char_list): if ch.lower() == name.lower(): return i raise ServerError('Character not found.')
def remove_ban(self, client, ip): try: try: int(ip) except ValueError: ipaddress.ip_address(ip) ip = client.server.get_ipid(ip) except ValueError: if not len(ip) == 12: raise ServerError( 'Argument must be an IP address or 10-digit number.') del self.bans[ip] self.write_banlist()
def ooc_cmd_judgelog(client, arg): if not client.is_mod: raise ClientError('You must be authorized to do that.') if len(arg) != 0: raise ArgumentError('This command does not take any arguments.') jlog = client.area.judgelog if len(jlog) > 0: jlog_msg = '== Judge Log ==' for x in jlog: jlog_msg += '\r\n{}'.format(x) client.send_host_message(jlog_msg) else: raise ServerError( 'There have been no judge actions in this area since start of session.' )
def load_music(self, music_list_file='config/music.yaml', server_music_list=True): try: with open(music_list_file, 'r', encoding='utf-8') as music: music_list = yaml.safe_load(music) except FileNotFoundError: raise ServerError( 'Could not find music list file {}'.format(music_list_file)) if server_music_list: self.music_list = music_list self.build_music_pages_ao1() self.build_music_list_ao2(music_list=music_list) return music_list
def ooc_cmd_cleartesti(client, arg): """ Clears the testimony list, deleting all statements. Very handy, since otherwise you'd have to rewrite the testimony with a 1-statement new one and remove that statement manually. For mods and CM use only to prevent abuse. Usage: /cleartesti """ if len(arg) != 0: raise ArgumentError('This command does not take any arguments.') testi = list(client.area.testimony.statements) if len(testi) <= 1: raise ServerError('There is no testimony in this area.') else: client.area.testimony.statements = [] client.area.testimony.title = '' client.send_ooc('You have cleared the testimony.')
def ooc_cmd_judgelog(client, arg): """ List the last 10 uses of judge controls in the current area. Usage: /judgelog """ if len(arg) != 0: raise ArgumentError('This command does not take any arguments.') jlog = client.area.judgelog if len(jlog) > 0: jlog_msg = '== Judge Log ==' for x in jlog: jlog_msg += f'\r\n{x}' client.send_ooc(jlog_msg) else: raise ServerError( 'There have been no judge actions in this area since start of session.' )
def get_song_data(self, music): """ Get information about a track, if exists. :param music: track name :returns: tuple (name, length or -1) :raises: ServerError if track not found """ for item in self.music_list: if item['category'] == music: return item['category'], -1 for song in item['songs']: if song['name'] == music: try: return song['name'], song['length'] except KeyError: return song['name'], -1 raise ServerError('Music not found.')
def make_multipoll(self, value): try: if [i for i in self.poll_list if i[0] == "{}".format(value)]: poll_selected = [i[1] for i in self.poll_list if i[0] == "{}".format(value)] output = ('{} \'{}\''.format("".join(poll_selected), value)) stream = open('storage/poll/{}.yaml'.format(output), 'r') stream2 = yaml.load(stream) stream2['multivote'] = not stream2['multivote'] with open('storage/poll/{}.yaml'.format(output), 'w') as votelist_file: yaml.dump(stream2, votelist_file, default_flow_style=False) return stream2['choices'] else: return None except FileNotFoundError: raise ServerError('The specified poll has no file associated with it.') except IndexError: return
def get_song_data(self, music, c=None): # The client's personal music list should also be a valid place to search # so search in there too if possible if c and c.music_list: valid_music = self.music_list + c.music_list else: valid_music = self.music_list for item in valid_music: if item['category'] == music: return item['category'], -1 for song in item['songs']: if song['name'] == music: try: return song['name'], song['length'] except KeyError: return song['name'], -1 raise ServerError('Music not found.')
def ooc_cmd_testimony(client, arg): """ List the current testimony in this area. Usage: /testimony """ if len(arg) != 0: raise ArgumentError('This command does not take any arguments.') testi = list(client.area.testimony.statements) if len(testi) <= 1: raise ServerError('There is no testimony in this area.') else: testi.pop(0) testi_msg = 'Testimony: ' + client.area.testimony.title i = 1 for x in testi: testi_msg += f'\r\n{i}: ' testi_msg += x[4] i = i + 1 client.send_ooc(testi_msg)