def find_by_linked(self, beid, beids = None, ips = None, names = None): try: if(beids == None): beids = set() if(ips == None): ips = set() if(names == None): names = set() self.c.execute("SELECT count(beid) FROM users WHERE beid='{}'".format(beid)) if self.c.fetchone()[0]==0: return {"beids": beids, "ips": ips, "names": names} entries = self.c.execute("SELECT * FROM users WHERE beid = '{}'".format(beid)) entries = entries.fetchall() for data in entries: beids.add(data[2]) ips.add(data[3]) names.add(data[1]) ip_list = self.c.execute("SELECT * FROM users WHERE ip = '{}'".format(data[3])) ip_list = ip_list.fetchall() for row in ip_list: ips.add(row[3]) if(row[1] not in names): names.add(row[1]) if(row[2] not in beids): beids.add(row[2]) r = self.find_by_linked(row[2], beids, ips, names) beids = beids.union(r["beids"]) ips = ips.union(r["ips"]) names = names.union(r["names"]) return {"beids": beids, "ips": ips, "names": names} except Exception as e: log.print_exc() log.error(e)
def setupRcon(self, serverMessage=None): try: Events = None if self.arma_rcon: Events = self.arma_rcon.Events.copy() del self.arma_rcon self.arma_rcon = bec_rcon.ARC( self.rcon_settings["ip"], self.rcon_settings["password"], self.rcon_settings["port"], {'timeoutSec': self.rcon_settings["timeoutSec"]}) if Events: #restore Event Handlers self.arma_rcon.Events = Events else: #Add Event Handlers self.arma_rcon.add_Event("received_ServerMessage", self.rcon_on_msg_received) self.arma_rcon.add_Event("on_disconnect", self.rcon_on_disconnect) if (serverMessage): self.arma_rcon.serverMessage = serverMessage else: #Extend the chat storage data = self.arma_rcon.serverMessage.copy() self.arma_rcon.serverMessage = deque(maxlen=500) #Default: 100 data.reverse() for d in data: self.arma_rcon.serverMessage.append(d) except Exception as e: log.print_exc() log.error(e)
async def playerConnected(self, event, data): try: player_name = data["event_match"].group(2) player_profileID = int(data["event_match"].group(3)) for i in range(2): for player in self.players: name = player[4] if (name.endswith(" (Lobby)")): #Strip lobby from name name = name[:-8] if name == player_name: beid = player[3] ip = player[1].split(":")[0] sql = """ UPDATE users SET profileid = ? WHERE name = ? AND beid = ? AND ip = ? AND stamp > date('now','-1 day')""" log.debug(sql) self.c.execute(sql, (player_profileID, name, beid, ip)) self.con.commit() break await asyncio.sleep(60) except Exception as e: log.print_exc() log.error(e)
async def mission_script_error(self, event, stime, text, regxm, line): try: if (time() - self.mission_error_last < 60 * 10): self.mission_error_suppressed += 1 return self.mission_error_last = time() if (self.mission_error_suppressed > 0): await self.channel.send( ":warning: {} Errors were suppressed\n``{}`` ``{}`` line ``{}``\nAdditional Errors will be suppressed for 10min." .format(self.mission_error_suppressed, text, self.readLog.current_log, line)) else: await self.channel.send( ":warning: ``{}`` ``{}`` line ``{}``\nAdditional Errors will be suppressed for 10min." .format(text, self.readLog.current_log, line)) self.mission_error_suppressed = 0 #regex = "Error in expression <(?P<expression>.*?)>.*?Error position: <(?P<position>.*?)>.*?Error Undefined variable in expression: (?P<err_cause>.*?)File (?P<file>.*?)\.\.\., line (?P<line>[0-9]*)" #m = re.match(regex, error, flags=re.S) # if m: # await self.channel.send("{}... line {}\nAdditional Errors will be suppressed for 10min.".format(text, line) # else: # await self.channel.send("```sqf\n{}```".format(error)) except Exception as e: log.print_exc() log.error(e)
async def watch_log(self): try: update_counter = 0 while (True): #Wait till a log file exsists logs = self.getLogs() if (len(logs) > 0): self.current_log = logs[-1] log.info("current log: " + self.current_log) self.currentLinePos = 0 file = open(self.log_path + self.current_log, "r") for i, l in enumerate(file): pass self.currentLinePos = i + 1 #file.seek(0, 2) #jump to the end of the file try: while (True): #where = file.tell() try: line = file.readline() update_counter += 1 except: line = None if not line: await asyncio.sleep(1) #file.seek(where) if ( update_counter >= 60 ): #only check for new log files every 60s, to reduce IOPS update_counter = 0 if (self.current_log != self.getLogs()[-1]): old_log = self.current_log self.current_log = self.getLogs()[ -1] #update to new recent log self.currentLinePos = 0 file = open( self.log_path + self.current_log, "r") log.info("current log: " + self.current_log) self.EH.check_Event( "Log new", old_log, self.current_log) else: self.currentLinePos += 1 self.line = line #access to last read line (debugging) self.processLogLine(line) except (KeyboardInterrupt, asyncio.CancelledError): log.info("[asyncio] exiting {}".format(watch_log)) except Exception as e: log.error(e) log.print_exc() else: await asyncio.sleep(10 * 60) except (KeyboardInterrupt, asyncio.CancelledError): log.info("[asyncio] exiting {}".format(watch_log)) except Exception as e: log.print_exc() log.error(e)
def rcon_on_msg_received(self, args): try: message = args[0].strip() if (message.startswith("Player #")): #log.info(message) if (self.cfg["send_player_connect_msg"]): #"disconnect" if (message.endswith(" disconnected") and ":" not in message): if (self.cfg["send_player_connect_msg_hide_details"]): message = ":x: " + " ".join( message.split(" ")[2:-1]) asyncio.ensure_future(self.channel.send(message)) #"connect" elif (message.endswith(") connected")): if (self.cfg["send_player_connect_msg_hide_details"]): msg = "(".join(message.split( "(")[:-1]) #removes the last block with the ip msg = ":white_check_mark: " + " ".join( msg.split(" ")[2:]) else: msg = message asyncio.ensure_future(self.channel.send(msg)) except Exception as e: log.print_exc() log.error(e)
async def checkPermission(self, rctx, cmd): try: cmd = "command_{}".format(cmd) pr = self.PermissionConfig.cfgPermissions_Roles role = "@everyone" #check if everybody can use it if (cmd in pr[role] and pr[role][cmd]): #anyone can use the cmd return True #check if user can use it #Lookup user in linked accounts: for user_id, data in self.user_data.items(): log.info("{} {}".format(data["account_arma3"], rctx.user_guid)) if ("account_arma3" in data and data["account_arma3"][0] == rctx.user_guid): #check if user has permission: server = self.bot.guilds[0] user = discord.utils.get(server.members, id=int(user_id)) if (user): #get user roles, and check if role has permission for role in user.roles: if str(role) in pr.keys(): if (cmd in pr[str(role)] and pr[str(role)][cmd]): return True return False except Exception as e: log.print_exc() log.error(e) return False
async def memory_guard(self): while True: try: if (self.cfg["server_memory_protection"]): if (psutil.virtual_memory().percent > 70): await self.CommandRcon.arma_rcon.command( "#shutdownaftermission") await self.CommandRcon.arma_rcon.sayGlobal( "A Server shutdown has been scheduled at the end of this mission." ) if (self.memoryRestart == False): await self.channel.send( ":warning: Memory usage exceeded! Server shutdown scheduled after mission end" ) self.memoryRestart = True log.warning( ":warning: Memory usage exceeded! Server shutdown scheduled after mission end" ) elif ( psutil.virtual_memory().percent > 95 ): #might be too aggressive should short memory spikes occur await self.CommandRcon.arma_rcon.shutdown() log.warning( ":warning: Memory usage exceeded! Server was forced shutdown" ) except Exception as e: log.print_exc() log.error(e) await asyncio.sleep(10 * 60)
def pre_scan(self): try: if (self.maxMissions <= 0): return #disable Event handlers, so they dont trigger self.EH.disabled = True logs = self.getLogs() tempdataMissions = deque(maxlen=self.maxMissions) #scan most recent log. Until enough data is collected #go from newest to oldest log until the data buffer is filled for _log in reversed(logs): log.info("Pre-scanning: " + _log) self.scanfile(_log) if (len(tempdataMissions) + len(self.Missions) <= self.maxMissions): tempdataMissions.extendleft(reversed(self.Missions)) self.Missions = deque(maxlen=self.maxMissions) self.Missions.append({"dict": {}, "data": []}) else: break if (len(tempdataMissions) >= self.maxMissions): break self.Missions = tempdataMissions self.EH.disabled = False except Exception as e: log.print_exc() log.error(e)
def processLogLine(self, line): payload = {} try: timestamp, msg = self.splitTimestamp(line) payload["timestamp"] = timestamp payload["msg"] = msg payload["currentLinePos"] = self.currentLinePos self.EH.check_Event("Log line", payload) event, event_match = self.check_log_events(msg, self.events) # if(self.EH.disabled==False): # log.info("{} {} {}".format(line, event, event_match)) #log.info(event, msg, self.multiEventLockData) if (event_match): payload["event_match"] = event_match self.EH.check_Event(event, payload) if ("clutter" not in event): #log.info("{} {}".format(event, event_match)) self.processMission(event, (timestamp, msg, event_match)) self.EH.check_Event("Log line filtered", payload) else: self.processMission("", (timestamp, msg)) self.EH.check_Event("Log line filtered", payload) except Exception as e: log.print_exc() log.error(e)
def processMission(self, event, data): try: #new mission is being started if (event == "Mission readname"): self.Missions.append({ "dict": { "Server sessionID": self.server_sessionID, event: data }, "data": [] }) elif (event == "Server sessionID"): self.server_sessionID = data[2].group(2) #mission is complete, switching to between mission block elif (event == "Mission finished" or event == "Mission restarted"): log.info("{} {}".format( self.Missions[-1]["dict"]["Mission id"][0], self.Missions[-1]["dict"]["Mission id"][1])) self.Missions[-1]["dict"][event] = data self.Missions.append({ "dict": { "Server sessionID": self.server_sessionID }, "data": [] }) #process data within a mission elif ("Mission" in event): self.Missions[-1]["dict"][event] = data self.Missions[-1]["data"].append(data) except Exception as e: log.print_exc() log.error(e)
async def on_ready(self): try: await self.bot.wait_until_ready() self.CommandRcon = self.bot.cogs["CommandRcon"] # self.bot.cogs["CommandRcon"].arma_rcon = self.CommandRcon.arma_rcon except Exception as e: log.error(e)
async def on_ready(self): await self.bot.wait_until_ready() if("CommandRcon" not in self.bot.cogs): log.error("[module] 'CommandRcon' required, but not found in '{}'. Module unloaded".format(type(self).__name__)) del self return self.CommandRcon = self.bot.cogs["CommandRcon"] asyncio.ensure_future(self.fetch_player_data_loop())
async def statusSetter(self): while True: try: if (self.cfg["set_custom_status"]): await self.set_status() except Exception as e: log.print_exc() log.error(e) await asyncio.sleep(60)
def upgrade_database(self): try: json_f = self.path + "/player_db.json" if (not os.path.isfile(json_f)): return #get the count of tables with the name self.c.execute( ''' SELECT count(name) FROM sqlite_master WHERE type='table' AND name='users' ''' ) if self.c.fetchone()[0] == 0: #load old table with open(json_f) as json_file: data_db = json.load(json_file) data = [] for key, item in data_db.items(): for _item in item: id = None name = None beid = None ip = None date = None if "ID" in _item: id = int(_item["ID"]) if "name" in _item: name = _item["name"] if "beid" in _item: beid = _item["beid"] if "ip" in _item: ip = _item["ip"] if "last-seen" in _item: date = _item["last-seen"] data.append((id, name, beid, ip, date)) self.c.execute(""" CREATE TABLE users ( id INTEGER NOT NULL, name TEXT, beid TEXT, ip TEXT, stamp DATETIME, profileid INTEGER ); """) #date yyyy-MM-dd HH:mm:ss sql = 'INSERT INTO users (id, name, beid, ip, stamp) values(?, ?, ?, ?, ?)' self.c.executemany(sql, data) self.con.commit() json_f_new = json_f.replace(".json", "_old.json") os.rename(json_f, json_f_new) log.info("*** Database has been upgraded! ***") except Exception as e: log.print_exc() log.error(e)
async def task_setStatus(self): while True: try: await asyncio.sleep(60) await self.setStatus() except (KeyboardInterrupt, asyncio.CancelledError): log.info("[asyncio] exiting", task_setStatus) except Exception as e: log.error("setting status failed", e) log.print_exc()
def test(self, msg): try: print(log) print(log.info) log.info(msg) log.warning(msg) log.error(msg) print(log.handlers) return "Sucess" except Exception as e: print(e)
async def saveErrors(self): while True: try: if ((self.mission_error_last + 60) < time()): with open(self.path + "/logs/script_errors.log", 'w+') as outfile: json.dump(self.script_errors, outfile, indent=4) except Exception as e: log.print_exc() log.error(e) await asyncio.sleep(60)
async def checkAFK(self, ctx, player_id: int): players = await self.arma_rcon.getPlayersArray() player_name = None for player in players: if (int(player[0]) == player_id): player_name = player[4] if (player_name.endswith(" (Lobby)")): #Strip lobby from name player_name = player_name[:-8] if (player_name == None): await ctx.send("Player not found") return msg = "Starting AFK check for: ``" + str(player_name) + "``" await ctx.send(msg) already_active = False for i in range(0, 300): #checks for 5min (10*30s) if (self.playerTypesMessage(player_name)): if (i == 0): already_active = True await ctx.send( "Player was recently active. Canceling AFK check.") else: await ctx.send( "Player responded in chat. Canceling AFK check.") if (already_active == False): await self.arma_rcon.sayPlayer( player_id, "Thank you for responding in chat.") return if ((i % 30) == 0): try: for k in range(0, 3): await self.arma_rcon.sayPlayer( player_id, "Type something in chat or you will be kicked for being AFK. (" + str(round(i / 30) + 1) + "/10)") except: log.error("Failed to send command sayPlayer (checkAFK)") await asyncio.sleep(1) if (self.playerTypesMessage(player_name)): if (i == 0): already_active = True await ctx.send("Player responded in chat. Canceling AFK check.") if (already_active == False): try: await self.arma_rcon.sayPlayer( player_id, "Thank you for responding in chat.") except: log.error("Failed to send command sayPlayer") return else: await self.arma_rcon.kickPlayer(player_id, "AFK too long") await ctx.send("``" + str(player_name) + "`` did not respond and was kicked for being AFK")
async def _system_res(self): try: self.system_res = deque(maxlen=1440*10) #1440 = 1 day while True: databuilder = {} databuilder["cpu"] = round(psutil.cpu_percent(),2) databuilder["ram"] = round(psutil.virtual_memory().percent,2) databuilder["swap"] = round(psutil.swap_memory().percent,2) databuilder["time"] = str(datetime.now().strftime("%Y-%m-%d %H-%M-%S")) self.system_res.append(databuilder) await asyncio.sleep(60) except Exception as e: log.error(e)
async def on_ready(self): try: await self.bot.wait_until_ready() self.CommandRcon = self.bot.cogs["CommandRcon"] self.channel = self.bot.get_channel(int(self.cfg["post_channel"])) if(self.cfg["report_script_errors"] and self.channel): self.readLog.EH.add_Event("Mission script error", self.mission_script_error) self.readLog.EH.add_Event("Log new", self.newLog) asyncio.ensure_future(self.readLog.watch_log()) asyncio.ensure_future(self.memory_guard()) except Exception as e: log.print_exc() log.error(e)
async def task_updatePlayerStats(self): await asyncio.sleep(60 * 2) while True: try: log.info("[JMW] Updating Player stats") t = threading.Thread(target=self.psg.generateStats()) t.start() self.psg_updated = datetime.datetime.now(datetime.timezone.utc) except (KeyboardInterrupt, asyncio.CancelledError): pass except Exception as e: log.error("Failed to update player stats", e) log.print_exc() await asyncio.sleep(60 * 60 * 24)
def scanfile(self, name): with open(self.log_path + name, encoding='utf-8', errors='replace') as fp: try: line = fp.readline() except: line = None while line: self.processLogLine(line) try: line = fp.readline() except Exception as e: log.print_exc() log.error(e) line = None
def rcon_on_msg_received(self, args): try: message = args[0] if (":" in message): header, body = message.split(":", 1) if (self.CommandRcon.isChannel(header) ): #was written in a channel player_name = header.split(") ")[1] else: #is join or disconnect, or similaar #log.info(message) asyncio.ensure_future(self.banned_user_kick(message)) except Exception as e: log.print_exc() log.error(e)
def alter_database(self): try: ## Alter table (upgrade by adding the profileid column) self.c.execute( "SELECT COUNT(*) AS CNTREC FROM pragma_table_info('users') WHERE name='profileid'" ) if self.c.fetchone()[0] == 0: sql = """ ALTER TABLE users ADD COLUMN profileid INTEGER;""" self.c.execute(sql) self.con.commit() log.info("Altered DB Table: 'Added COLUMN profileid'") except Exception as e: log.print_exc() log.error(e)
async def on_ready(self): await self.bot.wait_until_ready() self.channel = self.bot.get_channel(self.cfg["post_channel"]) if ("CommandRcon" not in self.bot.cogs): log.info( "[module] 'CommandRcon' required, but not found in '{}'. Module unloaded" .format(type(self).__name__)) del self return try: self.CommandRcon = self.bot.cogs["CommandRcon"] self.CommandRcon.arma_rcon.add_Event("received_ServerMessage", self.rcon_on_msg_received) except Exception as e: log.print_exc() log.error(e)
async def on_ready(self): await self.bot.wait_until_ready() if ("CommandRcon" not in self.bot.cogs): log.error( "[module] 'CommandRcon' required, but not found in '{}'. Module unloaded" .format(type(self).__name__)) del self return try: self.CommandArma = self.bot.cogs["CommandArma"] self.CommandArma.readLog.EH.add_Event("Player connected", self.playerConnected) except KeyError: self.CommandArma = None self.CommandRcon = self.bot.cogs["CommandRcon"] asyncio.ensure_future(self.fetch_player_data_loop())
async def fetch_player_data_loop(self): while True: await asyncio.sleep(60) try: if(self.CommandRcon.arma_rcon.disconnected==True): continue try: self.players = await self.CommandRcon.arma_rcon.getPlayersArray() except Exception as e: continue #self.player_db.save = False c_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") #Set status if(self.cfg["set_custom_status"]==True): await self.set_status(self.players) if(self.cfg["setTopicPlayerList_channel"]>0): await self.setTopicPlayerList(self.players) for player in self.players: name = player[4] if(name.endswith(" (Lobby)")): #Strip lobby from name name = name[:-8] d_row = { "id": player[0], "name": name, "beid": player[3], "ip": player[1].split(":")[0], #removes port from ip "note": None, "stamp": c_time } self.update_insert(d_row) #self.player_db.save = True #self.player_db.json_save() except Exception as e: log.print_exc() log.error(e)
async def mission_script_error(self, event, payload): try: text = payload["msg"] line = payload["currentLinePos"] stime = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S") if text in self.script_errors: self.script_errors[text]["log"] = self.readLog.current_log self.script_errors[text]["line"] = line self.script_errors[text]["count"] += 1 self.script_errors[text]["last"] = payload["timestamp"] self.script_errors[text]["positions"].append({ "log": self.readLog.current_log, "line": line, "time": stime }) else: self.script_errors[text] = { "log": self.readLog.current_log, "line": line, "count": 1, "last": stime, "positions": [{ "log": self.readLog.current_log, "line": line, "time": stime }] } await self.channel.send( ":warning: ``{}`` ``{}`` line ``{}``".format( text, self.readLog.current_log, line)) self.mission_error_last = time() except Exception as e: log.print_exc() log.error(e)
def loadCogs(bot): Modules.module_list = sorted(list(glob.glob("modules/*"))) for module in Modules.module_list: module = module.replace("\\", "/") try: cfg = Modules.loadCfg(module) if (cfg): CoreConfig.modules[module] = { Modules.general_settings_file: cfg } if (cfg["load_module"] == True): bot.load_extension( module.replace("/", ".") + ".module") else: log.info("[Modules] Skipped Cogs in '{}'".format(module)) except (discord.ClientException, ModuleNotFoundError) as e: log.print_exc() log.error(f'Failed to load extension: {module} ({e})') Modules.fix_wrappers()