def check_if_player_has_url_name(self, player_object=None): try: if player_object is None: player_object = self.bot.players.get(self.player_steamid) else: called_by_trigger = True self.bot = self if not self.bot.whitelist.player_is_allowed(player_object): p = re.search( r"[-A-Z0-9+&@#/%?=~_|!:,.;]{3,}\.[A-Z0-9+&@#/%=~_|]{2,3}$", player_object.name, re.IGNORECASE) if p: logger.info( "kicked player {} for having an URL in the name.".format( player_object.name)) self.tn.say( "{} has been kicked. we do not allow url-names!".format( player_object.steamid), color=self.bot.chat_colors['alert']) self.tn.kick( player_object, "We do not allow urls in names. Visit chrani.net/chrani-bot to find out what that means and if / what options are available to you!" ) except Exception as e: logger.error("{} encountered the error '{}'".format( player_object.name, e)) pass
def player_is_outside_boundary(self): try: player_object = self.bot.players.get(self.player_steamid) try: location_object = self.bot.locations.get('system', "lobby") except KeyError: return False if player_object.authenticated is not True: if not location_object.player_is_inside_boundary(player_object): if self.tn.teleportplayer(player_object, location_object): player_object.set_coordinates(location_object) self.bot.players.upsert(player_object) logger.info("{} has been ported to the lobby!".format( player_object.name)) self.tn.send_message_to_player( player_object, "You have been ported to the lobby! Authenticate with /password <password>", color=self.bot.chat_colors['alert']) self.tn.send_message_to_player( player_object, "see https://chrani.net/chrani-bot for more information!", color=self.bot.chat_colors['warning']) if self.tn.muteplayerchat(player_object, True): self.tn.send_message_to_player( player_object, "Your chat has been disabled!", color=self.bot.chat_colors['warning']) except Exception as e: logger.error(e) pass
def run_demo(): """ Starts a command-line based demo request loop for debugging. """ logger.info('Starting request loop') previous_pattern = None while True: try: text = input('User input: ') request = Request(text=text, previous_pattern=previous_pattern, mood=0.0, affection=0.0, bot_gender=Gender.FEMALE, bot_name='Lana', bot_birthdate=date(1995, 10, 5), bot_favorite_color='grün', father_name='Georg', father_age=49, mother_name='Agathe', mother_age=47) response = handle_request(request) print('Response: ', response.text) previous_pattern = response.pattern except KeyboardInterrupt: # Interrupt requested by user logger.info('Keyboard interrupt detected, aborting request loop') return except Exception as ex: logger.error('{}: {}'.format(type(ex).__name__, str(ex))) continue
def check_if_player_is_on_whitelist(self, player_object=None): try: if player_object is None: player_object = self.bot.players.get(self.player_steamid) else: called_by_trigger = True self.bot = self try: player_object = self.bot.players.load(player_object.steamid) except KeyError as e: logger.error("{} encountered the error '{}'".format( player_object.name, e)) if self.bot.whitelist.is_active(): if not self.bot.whitelist.player_is_allowed(player_object): logger.info( "kicked player {} for not being on the whitelist".format( player_object.name)) self.tn.say("{} has been kicked. This is VIP Only!".format( player_object.name), color=self.bot.chat_colors['alert']) self.tn.kick( player_object, "You are not on our whitelist. Visit chrani.net/chrani-bot to find out what that means and if / what options are available to you!" ) except Exception as e: logger.error("{} encountered the error '{}'".format( player_object.name, e)) pass
def __init__( self, host: str = 'mongo', port: str = '27017', username: str = 'user', password: str = 'pass', db: str = 'db' ) -> None: self.client = MongoClient( host=f'mongodb://{username}:{password}@{host}:{port}/' ) self.notifications = self.client[db].notifications logger.info('Database manager is set')
def polling(self): try: apihelper.proxy = proxies() logger.info('Bot is polling') self.bot.polling() except (ConnectionError, RemoteDisconnected, ConnectTimeout, ReadTimeout): self.bot.stop_polling() logger.error('Caught exception, restarting bot') self.polling()
def check_ip_country(self, player_object=None): try: if self.bot.settings_dict['ipinfo.io_password'] is None: return if player_object is None: player_object = self.bot.players.get(self.player_steamid) else: # the scope changes when called by the bots main-loop called_by_trigger = True self.bot = self # check if we already know the country code and check against whitelist and banned list users_country = player_object.get_country_code() if self.bot.whitelist.player_is_allowed(player_object) or ( users_country is not None and users_country not in self.bot.banned_countries_list): return False try: if users_country is None: f = urllib.urlopen( "https://ipinfo.io/" + player_object.ip + "/country?token=" + str(self.bot.settings_dict['ipinfo.io_password'])) users_country = f.read().rstrip() except Exception: logger.debug( "something went wrong in fetching the ipinfo dataset for player {}" .format(player_object.name)) try: player_object.set_country_code(users_country) self.bot.players.upsert(player_object, save=True) except Exception as e: logger.error("{} encountered the error '{}'".format( player_object.name, e)) if users_country in self.bot.banned_countries_list: if self.tn.kick( player_object, "Your IP seems to be from a blacklisted country. Visit chrani.net/chrani-bot to find out what that means and if / what options are available to you!" ): logger.info("kicked player {} for being from {}".format( player_object.name, users_country)) self.tn.say( "{} has been kicked. Blacklisted Country ({})!".format( player_object.name, users_country), color=self.bot.chat_colors['alert']) except Exception as e: logger.error("{} encountered the error '{}'".format( player_object.name, e)) pass
def __init__(self, event, bot, player_steamid): self.player_steamid = str(player_steamid) logger.info("thread started for player " + self.player_steamid) self.tn = TelnetConnection(bot, bot.settings_dict['telnet_ip'], bot.settings_dict['telnet_port'], bot.settings_dict['telnet_password']) self.bot = bot self.run_observers_interval = 1 self.stopped = event Thread.__init__(self)
def proxies() -> MappingProxyType: response = requests.get(url='https://mtpro.xyz/api/?type=socks') response.raise_for_status() link = response.json()[randint(0, 10)] logger.info('Proxies are set') logger.info(f'Current proxy is: socks5://' f'{link.get("ip")}:{link.get("port")}') return MappingProxyType({ 'http': f'socks5://{link.get("ip")}:{link.get("port")}', 'https': f'socks5://{link.get("ip")}:{link.get("port")}' })
def start_qq( no_gui=False, new_user=False, debug=False, vpath="./v.jpg", smart_qq_refer="http://d1.web2.qq.com/proxy.html?v=20030916001&callback=1&id=2", cookie_file="cookie.data", plugin_setting={ "plugin_root": "./plugins", "plugins": [ "pluginmanage", "test1" ], "timers": [ "timer_weather" ] }, dbhandler='sqlite:///message-record.db', ): bot = QQBot(vpath, smart_qq_refer, cookie_file) # update the modules and enbale utf-8 decoding. reload(sys) sys.setdefaultencoding("utf-8") # set the mode for logger. if debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) # login bot.login(no_gui) # initialze the handler handler = SuperHandler(dbhandle=dbhandler, workers=5) handler.update_group_list(bot) logger.info("Update group list...") # dbhandler = DBHandler() # initialize the plugin manager plmanager = PluginManager(plugin_setting["plugin_root"]) timermanager = PluginManager(plugin_setting["plugin_root"]) # load the plugins for plugin_name in plugin_setting["plugins"]: # plmanager.add_plugin(plugin_name) try: plmanager.add_plugin(plugin_name) except Exception, e: print(e) logger.error("Failed to load plugin: %s" % plugin_name)
def trigger_action(self, player_object, command): command_queue = [] if self.bot.player_actions is not None: for player_action in self.bot.player_actions: function_category = player_action["group"] function_name = getattr(player_action["action"], 'func_name') if (player_action["match_mode"] == "isequal" and player_action["command"]["trigger"] == command ) or (player_action["match_mode"] == "startswith" and command.startswith( player_action["command"]["trigger"])): function_object = player_action["action"] # chat_command = player_action["command"] function_parameters = eval( player_action["env"] ) # yes. Eval. It's my own data, chill out! command_queue.append([ function_object, function_parameters, function_name, function_category, command, player_action["essential"] ]) for command in command_queue: has_permission = self.bot.permissions.player_has_permission( player_object, command[2], command[3]) if (isinstance(has_permission, bool) and has_permission is True) or (command[5] is True): try: command[0](command[1]) except TypeError: try: command[0](*command[1]) except: logger.debug( "Player {} has executed {}:{} with '/{}', which lead to an unknown error" .format(player_object.name, command[3], command[2], command[4])) pass logger.info( "Player {} has executed {}:{} with '/{}'".format( player_object.name, command[3], command[2], command[4])) else: self.bot.tn.send_message_to_player( player_object, "Access denied, you need to be {}".format( has_permission)) logger.info( "Player {} denied trying to execute {}:{}".format( player_object.name, command[3], command[2])) if len(command_queue) == 0: logger.info( "Player {} tried the command '{}' for which I have no handler." .format(player_object.name, command))
def run_loop(): """ Starts a request loop that reads lines from stdin. Each line represents a new request in JSON format that will be parsed by the loop and handled by the request handler. The response returned by the request handler will again be formatted as a JSON string and written to stdout, including a newline character after every response. If an error is raised during parsing of the request data or the request handling itself, the current request will be aborted which is signaled by the 'error\n' string written to stdout. The loop will then wait for a new request. The loop can be interrupted by either closing the stdin pipe, resulting in an EOFError handled by the loop, or by sending a keyboard interrupt (Ctrl + C). """ logger.info('Starting request loop') # Setup streams for reading requests and writing responses input_stream = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', newline='\n') output_stream = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', newline='\n', line_buffering=True) while True: try: logger.debug('Waiting for request input') json_data = input_stream.readline() if json_data == '': # Empty string equals EOF for io.TextIOWrapper # Abort loop logger.info('EOF detected, aborting request loop') return logger.debug('Received request, parsing') request = parse_request(json_data) response = handle_request(request) output_stream.write(json.dumps(response._asdict()) + '\n') except KeyboardInterrupt: # Interrupt requested by developer logger.info('Keyboard interrupt detected, aborting request loop') return except Exception as ex: logger.error('{}: {}'.format(type(ex).__name__, str(ex))) # Pass error to Go and await next request print('error') continue
def __init__(self): self.settings_dict = self.load_bot_settings(args_dict['Database-file']) self.bot_name = self.get_setting_by_name('bot_name') logger.info("{} started".format(self.bot_name)) self.tn = TelnetConnection(self, self.get_setting_by_name('telnet_ip'), self.get_setting_by_name('telnet_port'), self.get_setting_by_name('telnet_password'), show_log_init=True) self.poll_tn = TelnetConnection( self, self.get_setting_by_name('telnet_ip'), self.get_setting_by_name('telnet_port'), self.get_setting_by_name('telnet_password')) self.player_actions = actions_spawn + actions_whitelist + actions_authentication + actions_locations + actions_home + actions_backpack + actions_lobby + actions_dev self.observers = observers_whitelist + observers_dev + observers_lobby + observers_locations self.players = Players( ) # players will be loaded on a need-to-load basis self.listplayers_interval = 1.5 self.listplayers_interval_idle = self.listplayers_interval * 10 self.active_player_threads_dict = {} self.whitelist = Whitelist() if self.get_setting_by_name('whitelist_active') is not None: self.whitelist.activate() self.locations = Locations() self.passwords = { "authenticated": 'openup', "donator": 'blingbling', "mod": 'hoopmeup', "admin": 'ecvrules' } self.permission_levels_list = [ 'admin', 'mod', 'donator', 'authenticated', None ] self.permissions = Permissions(self.player_actions, self.permission_levels_list) self.load_from_db() self.chat_colors = { "standard": "ffffff", "info": "4286f4", "success": "00ff04", "error": "8c0012", "warning": "ffbf00", "alert": "ba0085", "background": "cccccc", } self.match_types = { # matches any command a player issues in game-chat 'chat_commands': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF Chat: '(?P<player_name>.*)': /(?P<command>.+)", 'chat_commands_coppi': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF GameMessage handled by mod ('Coppis command additions'|'Coppis command additions Light'): Chat: '(?P<player_name>.*)': /(?P<command>.*)", # player joined / died messages etc 'telnet_events_player': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF Player (?P<command>.*): (?P<steamid>\d+)", 'telnet_events_player_gmsg': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF GMSG: Player '(?P<player_name>.*)' (?P<command>.*)" } self.match_types_system = { # captures the response for telnet commands. used for example to capture teleport response 'telnet_commands': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF Executing command\s'(?P<telnet_command>.*)'\s((?P<source>by Telnet|from client))\s(?(source)from(?P<ip>.*):(?P<port>.*)|(?P<player_steamid>.*))", # the game logs several player-events with additional information (for now i only capture the one i need, but there are several more useful ones 'telnet_events_playerspawn': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF PlayerSpawnedInWorld \(reason: (?P<command>.+?), position: (?P<pos_x>.*), (?P<pos_y>.*), (?P<pos_z>.*)\): EntityID=(?P<entity_id>.*), PlayerID='(?P<steamid>.*), OwnerID='(?P<owner_steamid>.*)', PlayerName='(?P<player_name>.*)'", # isolates the disconnected log entry to get the total session time of a player easily 'telnet_player_disconnected': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF Player (?P<player_name>.*) (?P<command>.*) after (?P<time>.*) minutes", # to parse the telnets listplayers response 'listplayers_result_regexp': r"\d{1,2}. id=(\d+), (.+), pos=\((.?\d+.\d), (.?\d+.\d), (.?\d+.\d)\), rot=\((.?\d+.\d), (.?\d+.\d), (.?\d+.\d)\), remote=(\w+), health=(\d+), deaths=(\d+), zombies=(\d+), players=(\d+), score=(\d+), level=(\d+), steamid=(\d+), ip=(.*), ping=(\d+)\r\n", # to parse the telnets getgameprefs response 'getgameprefs_result_regexp': r"GamePref\.ConnectToServerIP = (?P<server_ip>.*)\nGamePref\.ConnectToServerPort = (?P<server_port>.*)\n", # player joined / died messages 'telnet_events_player_gmsg': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF GMSG: Player '(?P<player_name>.*)' (?P<command>.*)", # pretty much the first usable line during a players login #'eac_register_client': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF \[EAC\] Log: \[RegisterClient\] Client: (?P<client>.*) PlayerGUID: (?P<player_id>.*) PlayerIP: (?P<player_ip>.*) OwnerGUID: (?P<owner_id>.*) PlayerName: (?P<player_name>.*)", # player is 'valid' from here on #'eac_successful': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF EAC authentication successful, allowing user: EntityID=(?P<entitiy_id>.*), PlayerID='(?P<player_id>.*)', OwnerID='(?P<owner_id>.*)', PlayerName='(?P<player_name>.*)'" 'telnet_player_connected': r"^(?P<datetime>.+?) (?P<stardate>.+?) INF Player (?P<command>.*), entityid=(?P<entity_id>.*), name=(?P<player_name>.*), steamid=(?P<player_id>.*), steamOwner=(?P<owner_id>.*), ip=(?P<player_ip>.*)" } self.banned_countries_list = [ 'CN', 'CHN', 'KP', 'PRK', 'RU', 'RUS', 'NG', 'NGA' ] self.server_settings_dict = self.get_game_preferences()
def run(self): self.is_active = True # this is set so the main loop can be started / stopped self.tn.togglechatcommandhide("/") listplayers_dict = {} list_players_timeout_start = 0 listplayers_interval = self.listplayers_interval while self.is_active: if timeout_occurred(listplayers_interval, list_players_timeout_start): # get all currently online players and store them in a dictionary last_listplayers_dict = listplayers_dict listplayers_dict = self.poll_players() if len( listplayers_dict ) == 0: # adjust poll frequency when the server is empty listplayers_interval = self.listplayers_interval_idle else: listplayers_interval = self.listplayers_interval list_players_timeout_start = time.time() # prune players not online anymore for player in set(self.players.players_dict) - set( listplayers_dict.keys()): del self.players.players_dict[player] # create new player entries / update existing ones for player_steamid, player_dict in listplayers_dict.iteritems( ): try: player_object = self.players.get(player_steamid) # player is already online and needs updating player_object.update(**player_dict) if last_listplayers_dict != listplayers_dict: # but only if they have changed at all! """ we only update this if things have changed since this poll is slow and might be out of date. Any teleport issued by the bot or a player would generate more accurate data If it HAS changed it is by all means current and can be used to update the object. """ self.players.upsert(player_object) except KeyError: # player has just come online try: player_object = self.players.load(player_steamid) # player has a file on disc, update database! player_object.update(**player_dict) self.players.upsert(player_object) except KeyError: # player is totally new, create file! player_object = Player(**player_dict) self.players.upsert(player_object, save=True) # there should be a valid object state here now ^^ """ handle player-threads """ for player_steamid, player_object in self.players.players_dict.iteritems( ): """ start player_observer_thread for each player not already being observed """ if player_steamid not in self.active_player_threads_dict: player_observer_thread_stop_flag = Event() player_observer_thread = PlayerObserver( player_observer_thread_stop_flag, self, str(player_steamid) ) # I'm passing the bot (self) into it to have easy access to it's variables player_observer_thread.name = player_steamid # nice to have for the logs player_observer_thread.isDaemon() player_observer_thread.trigger_action( player_object, "entered the stream") player_observer_thread.start() # self.players.upsert(player_object, save=True) self.active_player_threads_dict.update({ player_steamid: { "event": player_observer_thread_stop_flag, "thread": player_observer_thread } }) for player_steamid in set( self.active_player_threads_dict) - set( self.players.players_dict.keys()): """ prune all active_player_threads from players no longer online """ active_player_thread = self.active_player_threads_dict[ player_steamid] stop_flag = active_player_thread["thread"] stop_flag.stopped.set() del self.active_player_threads_dict[player_steamid] """ since telnet_lines can contain one or more actual telnet lines, we add them to a queue and pop one line at a time. I hope to minimize the risk of a clogged bot this way, it might result in laggy commands. I shall have to monitor that """ try: telnet_lines = self.tn.read_line() except Exception as e: logger.error(e) raise IOError self.telnet_lines_list = deque() if telnet_lines is not None: for line in telnet_lines: self.telnet_lines_list.append( line) # get the current global telnet-response try: telnet_line = self.telnet_lines_list.popleft() except IndexError: telnet_line = None if telnet_line is not None: m = re.search(self.match_types_system["telnet_commands"], telnet_line) if not m or m and m.group('telnet_command') != 'lp': if telnet_line != '': logger.debug(telnet_line) """ send telnet_line to player-thread check 'chat' telnet-line(s) for any known playername currently online """ for player_steamid, player_object in self.players.players_dict.iteritems( ): possible_action_for_player = re.search( player_object.name, telnet_line) if possible_action_for_player: if player_steamid in self.active_player_threads_dict: active_player_thread = self.active_player_threads_dict[ player_steamid] active_player_thread[ "thread"].trigger_action_by_telnet(telnet_line) """ work through triggers caused by telnet_activity """ # telnet_player_connected is the earliest usable player-data line available, perfect spot to fire off triggers for whitelist and blacklist and such m = re.search( self.match_types_system["telnet_player_connected"], telnet_line) if m: try: player_object = self.players.load(m.group("player_id")) except KeyError: player_dict = { 'entityid': int(m.group("entity_id")), 'steamid': m.group("player_id"), 'name': m.group("player_name"), 'ip': m.group("player_ip") } player_object = Player(**player_dict) logger.info( "found player '{}' in the stream, accessing matrix...". format(player_object.name)) command_queue = [] for observer in self.observers: if observer[ 0] == 'trigger': # we only want the triggers here observer_function_name = observer[2] observer_parameters = eval( observer[3] ) # yes. Eval. It's my own data, chill out! command_queue.append( [observer_function_name, observer_parameters]) for command in command_queue: try: command[0](*command[1]) except TypeError: command[0](command[1]) time.sleep(0.05) # to limit the speed a bit ^^
try: timermanager.add_plugin(timer_name) except Exception, e: print(e) logger.error("Failed to load plugin: %s" % plugin_name) # register the plugins to the message handlers for (name, plugin) in plmanager.plugins.items(): handler.add_handler(name, plugin) timers = {} for (name, plugin) in timermanager.plugins.items(): t = PluginTimer(plugin, args=(bot, ), sleep=2) t.start() timers[name] = t logger.info("plugin available: %s" % plmanager.plugins.keys()) # main loop, query new messages and handle them. while True: # query the plugins to be updated. try: tobeupdated = plmanager.update_plugin() if tobeupdated: logger.info("changes are detected...try to update: [%s]" % ",".join(tobeupdated)) for each in tobeupdated: logger.info("update plugin: %s" % each) handler.update_handler(each, plmanager.plugins[each]) except Exception, e: logger.error("Fail to update the plugins.") try: tobeupdated = timermanager.update_plugin()
from bot.logger import logger from bot.model_definitions import Mode # Download the nltk model required for the tokenizer. # Will be skipped if the model has already been downloaded. nltk.download('punkt', quiet=True) # Check if the bot has been executed with a command-line arg, i.e. the target target = argv[1] if len(argv) > 1 else None if target == 'train-patterns': # Trains the pattern recognizer from bot.trainer import train_model logger.info('Running pattern training') train_model(Mode.PATTERNS) elif target == 'train-sentiments': # Trains the sentiment analysis (mood, affection) models from bot.trainer import train_model logger.info( 'Running sentiments analysis through moods and affections analysis training' ) train_model(Mode.AFFECTIONS) train_model(Mode.MOODS) elif target == 'train-chat': # Trains the chat bots text generator with previously parsed whatsapp chats from bot.text_processor.train import train
def switch_off(self, source=None): self.is_responsive = False if source is not None: logger.info("switched off player '{}' - {}".format( self.name, source))