From 2415a8146da517fc9eac506c9887036696169847 Mon Sep 17 00:00:00 2001 From: crate Date: Thu, 5 Jun 2025 16:25:45 -0500 Subject: [PATCH 1/2] looks good --- garfmain.py | 88 ++++++++++++++-------------------------------- garfpy/__init__.py | 9 ++--- garfpy/iputils.py | 79 ++++++++++++++++++++++++++++++++--------- 3 files changed, 93 insertions(+), 83 deletions(-) diff --git a/garfmain.py b/garfmain.py index 4b900a0..e45462a 100644 --- a/garfmain.py +++ b/garfmain.py @@ -1,10 +1,9 @@ import config import asyncio import discord -import subprocess from garfpy import( - logger, is_private, + logger, IPUtils, aod_message, generate_qr, Kroger, GarfAI, GarfbotRespond) @@ -22,9 +21,11 @@ garfbot = discord.Client(intents=intents) garf_respond = GarfbotRespond() garfield = GarfAI() +iputils = IPUtils() kroger = Kroger() + @garfbot.event async def on_ready(): try: @@ -37,28 +38,18 @@ async def on_ready(): @garfbot.event async def on_message(message): - - content = message.content.strip() - lower = content.lower() - user = message.author.name - guild = message.guild.name if message.guild else "Direct Message" - guild_id = message.guild.id - - # Chats & pics if message.author == garfbot.user: return + + content = message.content.strip() + lower = content.lower() + user_name = message.author.name + guild_id = message.guild.id + guild_name = message.guild.name if message.guild else "Direct Message" - if lower.startswith("hey garfield") or isinstance(message.channel, discord.DMChannel): - prompt = content[12:] if lower.startswith("hey garfield") else message.content - answer = await garfield.generate_chat(prompt) - logger.info(f"Chat Request - User: {user}, Server: {guild}, Prompt: {prompt}") - await message.channel.send(answer) - - if lower.startswith('garfpic '): - prompt = content[8:] - logger.info(f"Image Request - User: {user}, Server: {guild}, Prompt: {prompt}") - await message.channel.send(f"`Please wait... image generation queued: {prompt}`") - await garfield.garfpic(message, prompt) + # IP utils + if message.guild and lower.startswith(("garfping ", "garfdns ", "garfhack ")): + await iputils.scan(message, user_name, guild_name, lower) # Wikipedia if lower.startswith('garfwiki '): @@ -80,47 +71,6 @@ async def on_message(message): logger.error(e) await message.channel.send(e) - # IP utils - query = message.content.split() - target = query[-1] - - if lower.startswith("garfping "): - try: - logger.info(f"Ping Request - User: {user}, Server: {guild}, Target: {target}") - if is_private(target): - rejection = await garfield.generate_chat("Hey Garfield, explain to me why I am dumb for trying to hack your private computer network.") - await message.channel.send(rejection) - else: - result = subprocess.run(['ping', '-c', '4', target], capture_output=True, text=True) - await message.channel.send(f"`Ping result for {target}:`\n```\n{result.stdout}\n```") - except Exception as e: - await message.channel.send(f"`GarfBot Error: {str(e)}`") - - if lower.startswith("garfdns "): - try: - logger.info(f"NSLookup Request - User: {user}, Server: {guild}, Target: {target}") - if is_private(target): - rejection = await garfield.generate_chat("Hey Garfield, explain to me why I am dumb for trying to hack your private computer network.") - await message.channel.send(rejection) - else: - result = subprocess.run(['nslookup', target], capture_output=True, text=True) - await message.channel.send(f"`NSLookup result for {target}:`\n```\n{result.stdout}\n```") - except Exception as e: - await message.channel.send(f"`GarfBot Error: {str(e)}`") - - if lower.startswith("garfhack "): - try: - logger.info(f"Nmap Request - User: {user}, Server: {guild}, Target: {target}") - if is_private(target): - rejection = await garfield.generate_chat("Hey Garfield, explain to me why I am dumb for trying to hack your private computer network.") - await message.channel.send(rejection) - else: - await message.channel.send(f"`Scanning {target}...`") - result = subprocess.run(['nmap', '-Pn', '-O', '-v', target], capture_output=True, text=True) - await message.channel.send(f"`Ping result for {target}:`\n```\n{result.stdout}\n```") - except Exception as e: - await message.channel.send(f"`GarfBot Error: {str(e)}`") - # Kroger Shopping if lower.startswith("garfshop "): try: @@ -147,6 +97,20 @@ async def on_message(message): await message.channel.send(response) break + # Chats & pics + elif lower.startswith("hey garfield") or isinstance(message.channel, discord.DMChannel): + prompt = content[12:] if lower.startswith("hey garfield") else message.content + answer = await garfield.generate_chat(prompt) + logger.info(f"Chat Request - User: {user_name}, Server: {guild_name}, Prompt: {prompt}") + await message.channel.send(answer) + + elif lower.startswith('garfpic '): + prompt = content[8:] + logger.info(f"Image Request - User: {user_name}, Server: {guild_name}, Prompt: {prompt}") + await message.channel.send(f"`Please wait... image generation queued: {prompt}`") + await garfield.garfpic(message, prompt) + + # Run Garfbot async def garfbot_connect(): while True: diff --git a/garfpy/__init__.py b/garfpy/__init__.py index ce62920..0234976 100644 --- a/garfpy/__init__.py +++ b/garfpy/__init__.py @@ -2,9 +2,10 @@ from .log import logger from .kroger import Kroger -from .iputils import is_private -from .aod import aod_message -from .qr import generate_qr +# from .iputils import scan from .kroger import Kroger from .garfai import GarfAI -from .respond import GarfbotRespond \ No newline at end of file +from .respond import GarfbotRespond +from .aod import aod_message +from .qr import generate_qr +from .iputils import IPUtils \ No newline at end of file diff --git a/garfpy/iputils.py b/garfpy/iputils.py index 4f7d1d5..551734d 100644 --- a/garfpy/iputils.py +++ b/garfpy/iputils.py @@ -1,19 +1,64 @@ +import discord import ipaddress +import subprocess +from garfpy import logger -def is_private(target): - try: - ip_obj = ipaddress.ip_address(target) - if ip_obj.is_private: - return True - except ValueError: - if "crate.lan" in target.lower(): - return True - if "crate.zip" in target.lower(): - return True - if "memtec.org" in target.lower(): - return True - if "crateit.net" in target.lower(): - return True - if "garfbot.art" in target.lower(): - return True - return False + +class IPUtils: + def is_private(self, target): + try: + ip_obj = ipaddress.ip_address(target) + if ip_obj.is_private: + return True + except ValueError: + if "crate.lan" in target.lower(): + return True + if "crate.zip" in target.lower(): + return True + if "memtec.org" in target.lower(): + return True + if "crateit.net" in target.lower(): + return True + if "garfbot.art" in target.lower(): + return True + return False + + async def scan(self, message, user, guild, query): + split = query.split() + target = split[-1] + if self.is_private(target): + return + + if query.startswith("garfping "): + try: + logger.info(f"Ping Request - User: {user}, Server: {guild}, Target: {target}") + await message.channel.send(f"`Pinging {target}...`") + result = subprocess.run(['ping', '-c', '4', target], capture_output=True, text=True) + embed = discord.Embed(title=f"Ping result:", color=0x4d4d4d) + embed.add_field(name=target, value=f"```{result.stdout}```") + await message.channel.send(embed=embed) + except Exception as e: + await message.channel.send(f"`GarfBot Error: {str(e)}`") + + if query.startswith("garfdns "): + try: + logger.info(f"NSLookup Request - User: {user}, Server: {guild}, Target: {target}") + await message.channel.send(f"`Requesting {target}...`") + result = subprocess.run(['nslookup', target], capture_output=True, text=True) + embed = discord.Embed(title=f"NSLookup result:", color=0x4d4d4d) + embed.add_field(name=target, value=f"```{result.stdout}```") + await message.channel.send(embed=embed) + except Exception as e: + await message.channel.send(f"`GarfBot Error: {str(e)}`") + + if query.startswith("garfhack "): + try: + logger.info(f"Nmap Request - User: {user}, Server: {guild}, Target: {target}") + await message.channel.send(f"`Scanning {target}...`") + result = subprocess.run(['nmap', '-Pn', '-O', '-v', target], capture_output=True, text=True) + embed = discord.Embed(title=f"Nmap scan result:", color=0x4d4d4d) + embed.add_field(name=target, value=f"```{result.stdout}```") + embed.set_footer(text="https://nmap.org/") + await message.channel.send(embed=embed) + except Exception as e: + await message.channel.send(f"`GarfBot Error: {str(e)}`") \ No newline at end of file -- 2.47.2 From 6b9c2b638ab615899a925fefb4e6afaf77dd7126 Mon Sep 17 00:00:00 2001 From: crate Date: Thu, 5 Jun 2025 17:25:43 -0500 Subject: [PATCH 2/2] format etc --- garfmain.py | 75 ++++++++++++++--------- garfpy/__init__.py | 3 +- garfpy/aod.py | 149 +++++++++++++++++++++++++++------------------ garfpy/garfai.py | 38 ++++++------ garfpy/iputils.py | 36 +++++++---- garfpy/kroger.py | 55 ++++++++++------- garfpy/log.py | 17 +++--- garfpy/qr.py | 17 +++--- garfpy/respond.py | 110 +++++++++++++++++---------------- 9 files changed, 284 insertions(+), 216 deletions(-) diff --git a/garfmain.py b/garfmain.py index e45462a..ac6224c 100644 --- a/garfmain.py +++ b/garfmain.py @@ -2,10 +2,15 @@ import config import asyncio import discord -from garfpy import( - logger, IPUtils, - aod_message, generate_qr, - Kroger, GarfAI, GarfbotRespond) +from garfpy import ( + logger, + IPUtils, + aod_message, + generate_qr, + Kroger, + GarfAI, + GarfbotRespond, +) gapikey = config.GIF_TOKEN @@ -25,13 +30,14 @@ iputils = IPUtils() kroger = Kroger() - @garfbot.event async def on_ready(): try: garf_respond.load_responses() asyncio.create_task(garfield.process_image_requests()) - logger.info(f"Logged in as {garfbot.user.name} running {txtmodel} and {imgmodel}.") + logger.info( + f"Logged in as {garfbot.user.name} running {txtmodel} and {imgmodel}." + ) except Exception as e: logger.error(e) @@ -40,7 +46,7 @@ async def on_ready(): async def on_message(message): if message.author == garfbot.user: return - + content = message.content.strip() lower = content.lower() user_name = message.author.name @@ -52,13 +58,13 @@ async def on_message(message): await iputils.scan(message, user_name, guild_name, lower) # Wikipedia - if lower.startswith('garfwiki '): + if lower.startswith("garfwiki "): query = message.content[9:] summary = await garfield.wikisum(query) await message.channel.send(summary) # QR codes - if lower.startswith('garfqr '): + if lower.startswith("garfqr "): text = message.content[7:] if len(text) > 1000: await message.channel.send("❌ Text too long! Maximum 1000 characters.") @@ -80,36 +86,44 @@ async def on_message(message): except Exception as e: await message.channel.send(f"`GarfBot Error: {str(e)}`") + # Chats & pics + elif lower.startswith("hey garfield") or isinstance( + message.channel, discord.DMChannel + ): + prompt = content[12:] if lower.startswith("hey garfield") else message.content + answer = await garfield.generate_chat(prompt) + logger.info( + f"Chat Request - User: {user_name}, Server: {guild_name}, Prompt: {prompt}" + ) + await message.channel.send(answer) + + elif lower.startswith("garfpic "): + prompt = content[8:] + logger.info( + f"Image Request - User: {user_name}, Server: {guild_name}, Prompt: {prompt}" + ) + await message.channel.send( + f"`Please wait... image generation queued: {prompt}`" + ) + await garfield.garfpic(message, prompt) + # Army of Dawn Server only!! - if message.guild and message.guild.id == 719605634772893757: + elif message.guild and message.guild.id == 719605634772893757: await aod_message(garfbot, message) # Auto-responses - if message.guild: + elif message.guild: responses = garf_respond.get_responses(guild_id) - - if lower.startswith('garfbot response '): + + if lower.startswith("garfbot response "): await garf_respond.garfbot_response(message, content) return - + for trigger, response in responses.items(): if trigger.lower() in lower: await message.channel.send(response) break - # Chats & pics - elif lower.startswith("hey garfield") or isinstance(message.channel, discord.DMChannel): - prompt = content[12:] if lower.startswith("hey garfield") else message.content - answer = await garfield.generate_chat(prompt) - logger.info(f"Chat Request - User: {user_name}, Server: {guild_name}, Prompt: {prompt}") - await message.channel.send(answer) - - elif lower.startswith('garfpic '): - prompt = content[8:] - logger.info(f"Image Request - User: {user_name}, Server: {guild_name}, Prompt: {prompt}") - await message.channel.send(f"`Please wait... image generation queued: {prompt}`") - await garfield.garfpic(message, prompt) - # Run Garfbot async def garfbot_connect(): @@ -117,9 +131,10 @@ async def garfbot_connect(): try: await garfbot.start(garfkey) except Exception as e: - e = str(e) - logger.error(f"Garfbot couldn't connect! {e}") - await asyncio.sleep(60) + e = str(e) + logger.error(f"Garfbot couldn't connect! {e}") + await asyncio.sleep(60) + if __name__ == "__main__": asyncio.run(garfbot_connect()) diff --git a/garfpy/__init__.py b/garfpy/__init__.py index 0234976..a11fd0b 100644 --- a/garfpy/__init__.py +++ b/garfpy/__init__.py @@ -2,10 +2,9 @@ from .log import logger from .kroger import Kroger -# from .iputils import scan from .kroger import Kroger from .garfai import GarfAI from .respond import GarfbotRespond from .aod import aod_message from .qr import generate_qr -from .iputils import IPUtils \ No newline at end of file +from .iputils import IPUtils diff --git a/garfpy/aod.py b/garfpy/aod.py index 0013d55..e13b651 100644 --- a/garfpy/aod.py +++ b/garfpy/aod.py @@ -11,6 +11,7 @@ from collections import defaultdict meows_file = "meow_counts.json" stats_file = "user_stats.json" + def json_load(file_path, default): if os.path.isfile(file_path): with open(file_path, "r") as f: @@ -18,77 +19,105 @@ def json_load(file_path, default): else: return default + meow_counts = defaultdict(int, json_load(meows_file, {})) user_stats = json_load(stats_file, {}) + async def aod_message(garfbot, message): - if "meow" in message.content.lower(): - logger.info(f"Meow detected! {message.author.name} said: {message.content}") + if "meow" in message.content.lower(): + logger.info(f"Meow detected! {message.author.name} said: {message.content}") - meow_counts[str(message.author.id)] += 1 + meow_counts[str(message.author.id)] += 1 - with open(meows_file, "w") as f: - json.dump(dict(meow_counts), f) + with open(meows_file, "w") as f: + json.dump(dict(meow_counts), f) - if message.content.lower() == "meowcount": - response = f"My records show that <@{message.author.id}> has meowed {meow_counts[str(message.author.id)]} time(s). Have a nice day." - await message.channel.send(response) + if message.content.lower() == "meowcount": + response = f"My records show that <@{message.author.id}> has meowed {meow_counts[str(message.author.id)]} time(s). Have a nice day." + await message.channel.send(response) - if message.content.lower() == "top meowers": - top_meowers = sorted(meow_counts.items(), key=itemgetter(1), reverse=True)[:10] - embed = discord.Embed(title="Top Meowers :cat:", color=0x000000) - for i, (user_id, meow_count) in enumerate(top_meowers): - user = await garfbot.fetch_user(int(user_id)) - embed.add_field(name=f"{i+1}. {user.name}", value=f"{meow_count} meows", inline=False) - await message.channel.send(embed=embed) + if message.content.lower() == "top meowers": + top_meowers = sorted(meow_counts.items(), key=itemgetter(1), reverse=True)[ + :10 + ] + embed = discord.Embed(title="Top Meowers :cat:", color=0x000000) + for i, (user_id, meow_count) in enumerate(top_meowers): + user = await garfbot.fetch_user(int(user_id)) + embed.add_field( + name=f"{i + 1}. {user.name}", + value=f"{meow_count} meows", + inline=False, + ) + await message.channel.send(embed=embed) - if message.content.lower() == "checking in": - user_id = str(message.author.id) - if user_id in user_stats and user_stats[user_id]["check_in_time"] is not None: - await message.channel.send(f"{message.author.mention} You are already checked in. Please check out first.") - return + if message.content.lower() == "checking in": + user_id = str(message.author.id) + if user_id in user_stats and user_stats[user_id]["check_in_time"] is not None: + await message.channel.send( + f"{message.author.mention} You are already checked in. Please check out first." + ) + return - check_in_time = datetime.now().timestamp() - if user_id not in user_stats: - user_stats[user_id] = {"check_ins": 0, "total_time": 0, "check_in_time": None} - user_stats[user_id]["check_in_time"] = check_in_time - await message.channel.send(f"{message.author.mention} You have been checked in. Please mute your microphone.") + check_in_time = datetime.now().timestamp() + if user_id not in user_stats: + user_stats[user_id] = { + "check_ins": 0, + "total_time": 0, + "check_in_time": None, + } + user_stats[user_id]["check_in_time"] = check_in_time + await message.channel.send( + f"{message.author.mention} You have been checked in. Please mute your microphone." + ) - elif message.content.lower() == "checking out": - user_id = str(message.author.id) - if user_id not in user_stats or user_stats[user_id]["check_in_time"] is None: - await message.channel.send(f"{message.author.mention} You have not checked in yet. Please check in first.") - return + elif message.content.lower() == "checking out": + user_id = str(message.author.id) + if user_id not in user_stats or user_stats[user_id]["check_in_time"] is None: + await message.channel.send( + f"{message.author.mention} You have not checked in yet. Please check in first." + ) + return - check_out_time = datetime.now().timestamp() - check_in_time = user_stats[user_id]["check_in_time"] - time_delta = check_out_time - check_in_time - user_stats[user_id]["check_ins"] += 1 - user_stats[user_id]["total_time"] += time_delta - user_stats[user_id]["check_in_time"] = None + check_out_time = datetime.now().timestamp() + check_in_time = user_stats[user_id]["check_in_time"] + time_delta = check_out_time - check_in_time + user_stats[user_id]["check_ins"] += 1 + user_stats[user_id]["total_time"] += time_delta + user_stats[user_id]["check_in_time"] = None - with open("user_stats.json", "w") as f: - json.dump(user_stats, f) - await message.channel.send(f"{message.author.mention} You have been checked out. Your session was {time_delta:.2f} seconds.") + with open("user_stats.json", "w") as f: + json.dump(user_stats, f) + await message.channel.send( + f"{message.author.mention} You have been checked out. Your session was {time_delta:.2f} seconds." + ) - elif message.content.lower() == "stats": - stats_embed = discord.Embed(title="User stats :trophy:", color=0x000000) - sorted_user_stats = sorted(user_stats.items(), key=lambda x: x[1]["total_time"], reverse=True) - table_rows = [["Name", "Check-ins", "Total Time"]] - for user_id, stats in sorted_user_stats: - if stats["check_in_time"] is None: - total_time_seconds = stats["total_time"] - hours, total_time_seconds = divmod(total_time_seconds, 3600) - minutes, total_time_seconds = divmod(total_time_seconds, 60) - seconds, fractions = divmod(total_time_seconds, 1) - fractions_str = f"{fractions:.3f}"[2:] - username = garfbot.get_user(int(user_id)).name - table_rows.append([username, str(stats["check_ins"]), f"{int(hours)}h {int(minutes)}m {int(seconds)}s {fractions_str}ms"]) - else: - username = garfbot.get_user(int(user_id)).name - table_rows.append([username, "Currently checked in", "-"]) - table_columns = list(zip(*table_rows[1:])) - table_fields = table_rows[0] - for field, values in zip(table_fields, table_columns): - stats_embed.add_field(name=field, value="\n".join(values), inline=True) - await message.channel.send(embed=stats_embed) + elif message.content.lower() == "stats": + stats_embed = discord.Embed(title="User stats :trophy:", color=0x000000) + sorted_user_stats = sorted( + user_stats.items(), key=lambda x: x[1]["total_time"], reverse=True + ) + table_rows = [["Name", "Check-ins", "Total Time"]] + for user_id, stats in sorted_user_stats: + if stats["check_in_time"] is None: + total_time_seconds = stats["total_time"] + hours, total_time_seconds = divmod(total_time_seconds, 3600) + minutes, total_time_seconds = divmod(total_time_seconds, 60) + seconds, fractions = divmod(total_time_seconds, 1) + fractions_str = f"{fractions:.3f}"[2:] + username = garfbot.get_user(int(user_id)).name + table_rows.append( + [ + username, + str(stats["check_ins"]), + f"{int(hours)}h {int(minutes)}m {int(seconds)}s {fractions_str}ms", + ] + ) + else: + username = garfbot.get_user(int(user_id)).name + table_rows.append([username, "Currently checked in", "-"]) + table_columns = list(zip(*table_rows[1:])) + table_fields = table_rows[0] + for field, values in zip(table_fields, table_columns): + stats_embed.add_field(name=field, value="\n".join(values), inline=True) + await message.channel.send(embed=stats_embed) diff --git a/garfpy/garfai.py b/garfpy/garfai.py index d9f3459..2e3c883 100644 --- a/garfpy/garfai.py +++ b/garfpy/garfai.py @@ -17,16 +17,13 @@ class GarfAI: self.image_request_queue = asyncio.Queue() async def garfpic(self, message, prompt): - await self.image_request_queue.put({'message': message, 'prompt': prompt}) + await self.image_request_queue.put({"message": message, "prompt": prompt}) async def generate_image(self, prompt): try: - client = AsyncOpenAI(api_key = self.openaikey) + client = AsyncOpenAI(api_key=self.openaikey) response = await client.images.generate( - model=self.imgmodel, - prompt=prompt, - n=1, - size="1024x1024" + model=self.imgmodel, prompt=prompt, n=1, size="1024x1024" ) image_url = response.data[0].url return image_url @@ -37,14 +34,14 @@ class GarfAI: return f"`GarfBot Error: ({e.status_code}) - Monday`" except Exception as e: logger.error(e) - return f"`GarfBot Error: Lasagna`" + return "`GarfBot Error: Lasagna`" async def process_image_requests(self): async with aiohttp.ClientSession() as session: while True: request = await self.image_request_queue.get() - message = request['message'] - prompt = request['prompt'] + message = request["message"] + prompt = request["prompt"] image_url = await self.generate_image(prompt) if "GarfBot Error" not in image_url: logger.info("Downloading & sending image...") @@ -53,7 +50,7 @@ class GarfAI: image_data = await resp.read() ram_image = io.BytesIO(image_data) ram_image.seek(0) - timestamp = message.created_at.strftime('%Y%m%d%H%M%S') + timestamp = message.created_at.strftime("%Y%m%d%H%M%S") filename = f"{timestamp}_generated_image.png" sendfile = discord.File(fp=ram_image, filename=filename) try: @@ -69,14 +66,17 @@ class GarfAI: async def generate_chat(self, question): try: - client = AsyncOpenAI(api_key = self.openaikey) + client = AsyncOpenAI(api_key=self.openaikey) response = await client.chat.completions.create( model=self.txtmodel, messages=[ - {"role": "system", "content": "Pretend you are sarcastic Garfield."}, - {"role": "user", "content": f"{question}"} + { + "role": "system", + "content": "Pretend you are sarcastic Garfield.", + }, + {"role": "user", "content": f"{question}"}, ], - max_tokens=400 + max_tokens=400, ) answer = response.choices[0].message.content return answer.replace("an AI language model", "a cartoon animal") @@ -84,15 +84,17 @@ class GarfAI: return f"`GarfBot Error: {e}`" except openai.APIError as e: logger.info(e, flush=True) - return f"`GarfBot Error: Monday`" + return "`GarfBot Error: Monday`" except Exception as e: logger.info(e, flush=True) - return f"`GarfBot Error: Lasagna`" + return "`GarfBot Error: Lasagna`" async def wikisum(self, query): try: summary = wikipedia.summary(query) - garfsum = await self.generate_chat(f"Please summarize in your own words: {summary}") + garfsum = await self.generate_chat( + f"Please summarize in your own words: {summary}" + ) return garfsum except Exception as e: - return e \ No newline at end of file + return e diff --git a/garfpy/iputils.py b/garfpy/iputils.py index 551734d..1f69723 100644 --- a/garfpy/iputils.py +++ b/garfpy/iputils.py @@ -4,7 +4,7 @@ import subprocess from garfpy import logger -class IPUtils: +class IPUtils: def is_private(self, target): try: ip_obj = ipaddress.ip_address(target) @@ -31,10 +31,14 @@ class IPUtils: if query.startswith("garfping "): try: - logger.info(f"Ping Request - User: {user}, Server: {guild}, Target: {target}") + logger.info( + f"Ping Request - User: {user}, Server: {guild}, Target: {target}" + ) await message.channel.send(f"`Pinging {target}...`") - result = subprocess.run(['ping', '-c', '4', target], capture_output=True, text=True) - embed = discord.Embed(title=f"Ping result:", color=0x4d4d4d) + result = subprocess.run( + ["ping", "-c", "4", target], capture_output=True, text=True + ) + embed = discord.Embed(title=f"Ping result:", color=0x4D4D4D) embed.add_field(name=target, value=f"```{result.stdout}```") await message.channel.send(embed=embed) except Exception as e: @@ -42,10 +46,14 @@ class IPUtils: if query.startswith("garfdns "): try: - logger.info(f"NSLookup Request - User: {user}, Server: {guild}, Target: {target}") + logger.info( + f"NSLookup Request - User: {user}, Server: {guild}, Target: {target}" + ) await message.channel.send(f"`Requesting {target}...`") - result = subprocess.run(['nslookup', target], capture_output=True, text=True) - embed = discord.Embed(title=f"NSLookup result:", color=0x4d4d4d) + result = subprocess.run( + ["nslookup", target], capture_output=True, text=True + ) + embed = discord.Embed(title=f"NSLookup result:", color=0x4D4D4D) embed.add_field(name=target, value=f"```{result.stdout}```") await message.channel.send(embed=embed) except Exception as e: @@ -53,12 +61,16 @@ class IPUtils: if query.startswith("garfhack "): try: - logger.info(f"Nmap Request - User: {user}, Server: {guild}, Target: {target}") - await message.channel.send(f"`Scanning {target}...`") - result = subprocess.run(['nmap', '-Pn', '-O', '-v', target], capture_output=True, text=True) - embed = discord.Embed(title=f"Nmap scan result:", color=0x4d4d4d) + logger.info( + f"Nmap Request - User: {user}, Server: {guild}, Target: {target}" + ) + await message.channel.send(f"`Scanning {target}...`") + result = subprocess.run( + ["nmap", "-Pn", "-O", "-v", target], capture_output=True, text=True + ) + embed = discord.Embed(title=f"Nmap scan result:", color=0x4D4D4D) embed.add_field(name=target, value=f"```{result.stdout}```") embed.set_footer(text="https://nmap.org/") await message.channel.send(embed=embed) except Exception as e: - await message.channel.send(f"`GarfBot Error: {str(e)}`") \ No newline at end of file + await message.channel.send(f"`GarfBot Error: {str(e)}`") diff --git a/garfpy/kroger.py b/garfpy/kroger.py index b126033..66b7d8f 100644 --- a/garfpy/kroger.py +++ b/garfpy/kroger.py @@ -8,44 +8,51 @@ class Kroger: def __init__(self): self.client_id = config.CLIENT_ID self.client_secret = config.CLIENT_SECRET - self.auth = b64encode(f"{self.client_id}:{self.client_secret}".encode()).decode() + self.auth = b64encode( + f"{self.client_id}:{self.client_secret}".encode() + ).decode() def kroger_token(self): headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': f'Basic {self.auth}' + "Content-Type": "application/x-www-form-urlencoded", + "Authorization": f"Basic {self.auth}", } - response = requests.post('https://api.kroger.com/v1/connect/oauth2/token', headers=headers, data={ - 'grant_type': 'client_credentials', - 'scope': 'product.compact' - }) + response = requests.post( + "https://api.kroger.com/v1/connect/oauth2/token", + headers=headers, + data={"grant_type": "client_credentials", "scope": "product.compact"}, + ) response.raise_for_status() - return response.json()['access_token'] + return response.json()["access_token"] def find_store(self, zipcode, kroken): headers = { - 'Authorization': f'Bearer {kroken}', + "Authorization": f"Bearer {kroken}", } params = { - 'filter.zipCode.near': zipcode, - 'filter.limit': 1, + "filter.zipCode.near": zipcode, + "filter.limit": 1, } - response = requests.get('https://api.kroger.com/v1/locations', headers=headers, params=params) + response = requests.get( + "https://api.kroger.com/v1/locations", headers=headers, params=params + ) return response.json() def search_product(self, product, loc_id, kroken): logger.info(f"Searching for {product}...") headers = { - 'Authorization': f'Bearer {kroken}', + "Authorization": f"Bearer {kroken}", } params = { - 'filter.term': product, - 'filter.locationId': loc_id, - 'filter.limit': 10 + "filter.term": product, + "filter.locationId": loc_id, + "filter.limit": 10, } - response = requests.get('https://api.kroger.com/v1/products', headers=headers, params=params) + response = requests.get( + "https://api.kroger.com/v1/products", headers=headers, params=params + ) return response.json() def garfshop(self, query): @@ -55,15 +62,17 @@ class Kroger: product = query[-2] zipcode = query[-1] loc_data = self.find_store(zipcode, kroken) - loc_id = loc_data['data'][0]['locationId'] - store_name = loc_data['data'][0]['name'] + loc_id = loc_data["data"][0]["locationId"] + store_name = loc_data["data"][0]["name"] product_query = self.search_product(product, loc_id, kroken) - products = product_query['data'] - sorted_products = sorted(products, key=lambda item: item['items'][0]['price']['regular']) + products = product_query["data"] + sorted_products = sorted( + products, key=lambda item: item["items"][0]["price"]["regular"] + ) response = f"Prices for `{product}` at `{store_name}` near `{zipcode}`:\n" for item in sorted_products: - product_name = item['description'] - price = item['items'][0]['price']['regular'] + product_name = item["description"] + price = item["items"][0]["price"]["regular"] response += f"- `${price}`: {product_name} \n" return response except Exception as e: diff --git a/garfpy/log.py b/garfpy/log.py index 3c17d94..de01b30 100644 --- a/garfpy/log.py +++ b/garfpy/log.py @@ -1,19 +1,18 @@ import logging from logging.handlers import TimedRotatingFileHandler -logger = logging.getLogger('garflog') +logger = logging.getLogger("garflog") logger.setLevel(logging.INFO) -formatter=logging.Formatter( - '%(asctime)s [%(levelname)s] %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' - ) +formatter = logging.Formatter( + "%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S" +) file_handler = TimedRotatingFileHandler( - 'garfbot.log', - when='midnight', + "garfbot.log", + when="midnight", interval=1, backupCount=7, - delay=True # Counter-intuitively, this will flush output immediately - ) + delay=True, # Counter-intuitively, this will flush output immediately +) file_handler.setFormatter(formatter) console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) diff --git a/garfpy/qr.py b/garfpy/qr.py index ceb3877..0ac4b48 100644 --- a/garfpy/qr.py +++ b/garfpy/qr.py @@ -4,7 +4,7 @@ from io import BytesIO def calculate_qr_settings(text): text_length = len(text) - + if text_length <= 25: version = 1 box_size = 12 @@ -38,26 +38,27 @@ def calculate_qr_settings(text): else: version = None box_size = 3 - + return version, box_size + async def generate_qr(text): version, box_size = calculate_qr_settings(text) - + qr = qrcode.QRCode( version=version, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=box_size, border=4, ) - + qr.add_data(text) qr.make(fit=True) - + qr_image = qr.make_image(fill_color="black", back_color="white") - + img_buffer = BytesIO() - qr_image.save(img_buffer, format='PNG') + qr_image.save(img_buffer, format="PNG") img_buffer.seek(0) - return img_buffer \ No newline at end of file + return img_buffer diff --git a/garfpy/respond.py b/garfpy/respond.py index a7941e0..ab3e55d 100644 --- a/garfpy/respond.py +++ b/garfpy/respond.py @@ -8,16 +8,22 @@ import re class GarfbotRespond: def __init__(self): self.guild_responses = {} - self.responses_file = 'responses.json' + self.responses_file = "responses.json" def load_responses(self): if os.path.exists(self.responses_file): try: - with open(self.responses_file, 'r', encoding='utf-8') as f: + with open(self.responses_file, "r", encoding="utf-8") as f: self.guild_responses = json.load(f) - self.guild_responses = {int(k): v for k, v in self.guild_responses.items()} - total_responses = sum(len(responses) for responses in self.guild_responses.values()) - logger.info(f"Loaded responses for {len(self.guild_responses)} server(s), ({total_responses} total responses)") + self.guild_responses = { + int(k): v for k, v in self.guild_responses.items() + } + total_responses = sum( + len(responses) for responses in self.guild_responses.values() + ) + logger.info( + f"Loaded responses for {len(self.guild_responses)} server(s), ({total_responses} total responses)" + ) except Exception as e: logger.info(f"Error loading responses: {e}") self.guild_responses = {} @@ -27,10 +33,14 @@ class GarfbotRespond: def save_responses(self): try: save_data = {str(k): v for k, v in self.guild_responses.items()} - with open(self.responses_file, 'w', encoding='utf-8') as f: + with open(self.responses_file, "w", encoding="utf-8") as f: json.dump(save_data, f, indent=2, ensure_ascii=False) - total_responses = sum(len(responses) for responses in self.guild_responses.values()) - logger.info(f"Saved responses for {len(self.guild_responses)} servers ({total_responses} total responses)") + total_responses = sum( + len(responses) for responses in self.guild_responses.values() + ) + logger.info( + f"Saved responses for {len(self.guild_responses)} servers ({total_responses} total responses)" + ) except Exception as e: logger.info(f"Error saving responses: {e}") @@ -43,121 +53,113 @@ class GarfbotRespond: guild_id = message.guild.id logger.info(message.content) - + match = re.search(r'garfbot response add "(.+)" "(.+)"', content, re.IGNORECASE) if match: trigger = match.group(1) response_text = match.group(2) await self.add_response(message, guild_id, trigger, response_text) return - - match = re.search(r'garfbot response add (\S+) (.+)', content, re.IGNORECASE) + + match = re.search(r"garfbot response add (\S+) (.+)", content, re.IGNORECASE) if match: - trigger = match.group(1) + trigger = match.group(1) response_text = match.group(2) await self.add_response(message, guild_id, trigger, response_text) return - - match = re.search(r'garfbot\s+response\s+remove\s+(.+)', content, re.IGNORECASE) + + match = re.search(r"garfbot\s+response\s+remove\s+(.+)", content, re.IGNORECASE) if match: trigger = match.group(1).strip() await self.remove_response(message, guild_id, trigger) return - + if content.lower() == "garfbot response list": await self.list_responses(message, guild_id) return - + await message.channel.send( "**Garfbot Auto-Response Commands:**\n" - "`garfbot response add \"trigger\" \"response\"`\n" - "`garfbot response remove \"trigger\"`\n" + '`garfbot response add "trigger" "response"`\n' + '`garfbot response remove "trigger"`\n' "`garfbot response list`\n\n" "**Examples:**\n" - "`garfbot response add \"hi\" \"Hello there!\"`\n" - "`garfbot response add \"that's what\" \"That's what she said!\"`\n" - "`garfbot response remove \"hi\"`" + '`garfbot response add "hi" "Hello there!"`\n' + '`garfbot response add "that\'s what" "That\'s what she said!"`\n' + '`garfbot response remove "hi"`' ) async def add_response(self, message, guild_id, trigger, response_text): if not response_text or not trigger: await message.channel.send("❌ Trigger and response must not be null.") return - + responses = self.get_responses(guild_id) responses[trigger] = response_text self.guild_responses[guild_id] = responses self.save_responses() - - embed = discord.Embed( - title="✅ Auto-response Added.", - color=0x00ff00 - ) + + embed = discord.Embed(title="✅ Auto-response Added.", color=0x00FF00) embed.add_field(name="Trigger", value=f"`{trigger}`", inline=True) embed.add_field(name="Response", value=f"`{response_text}`", inline=True) embed.set_footer(text=f"Server: {message.guild.name}") - + await message.channel.send(embed=embed) async def remove_response(self, message, guild_id, trigger): responses = self.get_responses(guild_id) - + if trigger in responses: removed_response = responses[trigger] del responses[trigger] self.guild_responses[guild_id] = responses self.save_responses() - - embed = discord.Embed( - title="✅ Auto-response Removed.", - color=0xff6b6b - ) + + embed = discord.Embed(title="✅ Auto-response Removed.", color=0xFF6B6B) embed.add_field(name="Trigger", value=f"`{trigger}`", inline=True) embed.add_field(name="Response", value=f"`{removed_response}`", inline=True) embed.set_footer(text=f"Server: {message.guild.name}") - + await message.channel.send(embed=embed) return - + for key in responses.keys(): if key.lower() == trigger.lower(): removed_response = responses[key] del responses[key] self.guild_responses[guild_id] = responses self.save_responses() - - embed = discord.Embed( - title="✅ Auto-response Removed.", - color=0xff6b6b - ) + + embed = discord.Embed(title="✅ Auto-response Removed.", color=0xFF6B6B) embed.add_field(name="Trigger", value=f"`{key}`", inline=True) - embed.add_field(name="Response", value=f"`{removed_response}`", inline=True) + embed.add_field( + name="Response", value=f"`{removed_response}`", inline=True + ) embed.set_footer(text=f"Server: {message.guild.name}") - + await message.channel.send(embed=embed) return - - await message.channel.send(f"❌ No auto-response found for trigger: `{trigger}`") + + await message.channel.send( + f"❌ No auto-response found for trigger: `{trigger}`" + ) async def list_responses(self, message, guild_id): responses = self.get_responses(guild_id) - + if not responses: await message.channel.send("No auto-responses configured for this server.") return - + embed = discord.Embed( - title=f"Auto-Responses for {message.guild.name}", - color=0x3498db + title=f"Auto-Responses for {message.guild.name}", color=0x3498DB ) - + for i, (trigger, response) in enumerate(responses.items(), 1): display_response = response[:50] + "..." if len(response) > 50 else response embed.add_field( - name=f"{i}. `{trigger}`", - value=f"→ {display_response}", - inline=False + name=f"{i}. `{trigger}`", value=f"→ {display_response}", inline=False ) - + embed.set_footer(text=f"Total responses: {len(responses)}") await message.channel.send(embed=embed) -- 2.47.2