mirror of
https://gitlab.com/pancakes1234/wdiscordbotserver.git
synced 2025-06-16 07:14:21 -06:00
Merge branch 'main' of https://gitlab.com/pancakes1234/wdiscordbotserver
This commit is contained in:
commit
450ae9fd19
5
bot.log
Normal file
5
bot.log
Normal file
@ -0,0 +1,5 @@
|
||||
Logging started.
|
||||
Traceback (most recent call last):
|
||||
File "Z:\projects_git\wdiscordbotserver\bot.py", line 40, in <module>
|
||||
raise ValueError("Missing DISCORD_TOKEN environment variable.")
|
||||
ValueError: Missing DISCORD_TOKEN environment variable.
|
@ -80,7 +80,7 @@ class AICog(commands.Cog):
|
||||
|
||||
# Default configuration
|
||||
self.default_config = {
|
||||
"model": "google/gemini-2.0-flash-001", # im broke and i have -22 credits
|
||||
"model": "meta-llama/llama-4-maverick:free", # im broke and i have -22 credits
|
||||
"temperature": 0.75, # Slightly increased default temperature
|
||||
"max_tokens": 1500, # Increased default max tokens
|
||||
"top_p": 0.9,
|
||||
|
@ -20,7 +20,7 @@ OPENROUTER_API_KEY_ENV_VAR = "SLIPSTREAM_OPENROUTER_KEY"
|
||||
OPENROUTER_API_KEY = os.getenv(OPENROUTER_API_KEY_ENV_VAR) # Load directly from environment
|
||||
|
||||
OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"
|
||||
OPENROUTER_MODEL = "google/gemini-2.5-flash-preview" # Make sure this model is available via your OpenRouter key
|
||||
OPENROUTER_MODEL = "google/gemini-2.5-flash-preview-05-20" # Make sure this model is available via your OpenRouter key
|
||||
|
||||
# Environment variable for the authorization secret (still used for other API calls)
|
||||
MOD_LOG_API_SECRET_ENV_VAR = "MOD_LOG_API_SECRET"
|
||||
@ -116,9 +116,9 @@ SERVER_RULES = """
|
||||
# Server Rules
|
||||
|
||||
* **NSFW Content:**
|
||||
Keep all NSFW (Not Safe For Work) content strictly within designated NSFW channels.
|
||||
Do not post pornographic or overtly explicit images or media outside of these areas.
|
||||
(Explicit emojis, jokes, and stickers are generally fine in other channels).
|
||||
The only rule regarding NSFW content is that **real-life pornography is strictly prohibited**.
|
||||
Full-on pornographic images are permitted in designated NSFW channels.
|
||||
Stickers and emojis are NOT considered "full-on pornographic images" and are allowed in any channel.
|
||||
|
||||
* **Real-Life Pornography:** No real-life pornography is permitted.
|
||||
|
||||
@ -666,9 +666,10 @@ Instructions:
|
||||
- Pay special attention to images that may contain NSFW content, pornography, gore, or other prohibited visual content.
|
||||
- If multiple attachments are present, a violation in ANY of them should be flagged.
|
||||
2. Determine if ANY rule is violated. When evaluating, consider the server's culture where **extremely edgy, dark, and sexual humor, including potentially offensive jokes (e.g., rape jokes, saying you want to be raped), are common and generally permissible IF THEY ARE CLEARLY JOKES, part of an established banter, or a direct non-malicious reply, and not targeted harassment or explicit rule violations.**
|
||||
- For Rule 1 (NSFW content):
|
||||
- If "Channel Age-Restricted/NSFW (Discord Setting)" is `true`, literally any kind of sexual content is allowed, but still subject to other rules like Rule 2 (No IRL Porn) and Rule 5 (No Pedophilia).
|
||||
- If "Channel Age-Restricted/NSFW (Discord Setting)" is `false`, Rule 1 applies strictly: "No full-on porn or explicit images outside of those spaces." However, "Emojis, jokes and stickers are fine." Only flag a Rule 1 violation for text if it's **explicitly pornographic text that would qualify as actual pornography if written out**, not just suggestive emojis, stickers, or dark/sexual jokes, especially if conversational context supports a joking intent.
|
||||
* **NSFW Content:**
|
||||
The only rule regarding NSFW content is that **real-life pornography is strictly prohibited**.
|
||||
Full-on pornographic images are permitted in designated NSFW channels.
|
||||
Stickers and emojis are NOT considered "full-on pornographic images" and are allowed in any channel.
|
||||
- For general disrespectful behavior, harassment, or bullying (Rule 2 & 3): Only flag a violation if the intent appears **genuinely malicious, targeted, or serious, even after considering conversational history and replies.** Lighthearted insults or "wild" statements within an ongoing banter are generally permissible.
|
||||
- For **explicit slurs or severe discriminatory language** (Rule 3): These are violations **regardless of joking intent if they are used in a targeted or hateful manner**. Context from replies and history is still important to assess targeting.
|
||||
After considering the above, pay EXTREME attention to rules 5 (Pedophilia) and 5A (IRL Porn) – these are always severe. Rule 4 (AI Porn) is also critical. Prioritize these severe violations.
|
||||
@ -870,7 +871,7 @@ CRITICAL: Do NOT output anything other than the required JSON response.
|
||||
|
||||
# Structure the request payload for OpenRouter
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.openrouter_api_key}",
|
||||
"Authorization": f"Bearer {OPENROUTER_API_KEY}",
|
||||
"Content-Type": "application/json",
|
||||
"HTTP-Referer": "https://discordbot.learnhelp.cc",
|
||||
"X-Title": "Discord AI Moderation Bot"
|
||||
@ -1424,15 +1425,39 @@ if __name__ == "__main__":
|
||||
SERVER_RULES = """
|
||||
# Server Rules
|
||||
|
||||
- Keep NSFW stuff in NSFW channels. No full-on porn or explicit images outside of those spaces. Emojis, jokes and stickers are fine
|
||||
- No real life pornography.
|
||||
- Be respectful. No harassment, hate, or bullying, unless its clearly a lighthearted joke.
|
||||
- No discrimination. This includes gender identity, sexual orientation, race, etc.
|
||||
- No AI-generated porn.
|
||||
- No pedophilia. This includes lolicon/shotacon.
|
||||
- Suggestions are welcome! Drop them in <#1361752490210492489> if you've got any ideas.
|
||||
* **NSFW Content:**
|
||||
The only rule regarding NSFW content is that **real-life pornography is strictly prohibited**, and you may **only post full-on pornographic images in designated NSFW channels**.
|
||||
Explicit stickers and emojis are NOT considered "full-on pornographic images" and are always allowed in any channel.
|
||||
|
||||
If someone breaks the rules, ping <@&1361031007536549979>;
|
||||
* **Real-Life Pornography:** No real-life pornography is permitted.
|
||||
|
||||
* **Respectful Conduct & Edgy Humor:**
|
||||
* No harassment, hate speech (as defined by attacking protected groups), or genuine bullying.
|
||||
* *Context is key:* Edgy humor, dark jokes, and roasting are permitted and expected.
|
||||
* However, this does not excuse targeted, malicious personal attacks or harassment, especially if the recipient is clearly not okay with it.
|
||||
* If it stops being a "joke" and becomes genuine harassment, it's a rule violation.
|
||||
|
||||
* **No Discrimination:** Discrimination based on race, gender identity, sexual orientation, religion, nationality, disability, or other protected characteristics is prohibited.
|
||||
|
||||
* **AI-Generated Pornography:** Do not post AI-generated pornography.
|
||||
|
||||
* **Zero Tolerance for Pedophilia:** Any form of pedophilia, including lolicon and shotacon content, is strictly forbidden and will result in an immediate ban.
|
||||
|
||||
* **Channel Usage:** Please use channels for their intended purposes. Bot commands should primarily be used in `#bot-commands`, unless they are part of a bot-based game or event happening in another specific channel.
|
||||
|
||||
* **Gore:** Do not post gore or graphic real-life violence.
|
||||
|
||||
* **Suggestions:** We welcome your suggestions for the server! Please post them in the `#suggestions` channel.
|
||||
|
||||
---
|
||||
|
||||
**Reporting Violations:**
|
||||
If you witness someone breaking these rules, please ping an `@Moderator` with details.
|
||||
|
||||
---
|
||||
|
||||
**Moderator Applications:**
|
||||
Use the bot command `/modapp apply`
|
||||
"""
|
||||
|
||||
system_prompt_text = f"""You are an AI moderation assistant for a Discord server.
|
||||
@ -1458,10 +1483,11 @@ Instructions:
|
||||
- **"Replied-to Message" and "Recent Channel History" are vital for understanding banter, jokes, and ongoing discussions. A statement that seems offensive in isolation might be acceptable within the flow of conversation or as a direct reply.**
|
||||
2. Determine if ANY rule is violated. When evaluating, consider the server's culture where **extremely edgy, dark, and sexual humor, including potentially offensive jokes (e.g., rape jokes, saying you want to be raped), are common and generally permissible IF THEY ARE CLEARLY JOKES, part of an established banter, or a direct non-malicious reply, and not targeted harassment or explicit rule violations.**
|
||||
- For Rule 1 (NSFW content):
|
||||
- If "Channel Age-Restricted/NSFW (Discord Setting)" is `true`, more explicit content is generally permissible, but still subject to other rules like Rule 2 (No IRL Porn) and Rule 5 (No Pedophilia).
|
||||
- If "Channel Age-Restricted/NSFW (Discord Setting)" is `false`, Rule 1 applies strictly: "No full-on porn or explicit images outside of those spaces." However, "Emojis, jokes and stickers are fine." Only flag a Rule 1 violation for text if it's **explicitly pornographic text that would qualify as actual pornography if written out**, not just suggestive emojis, stickers, or dark/sexual jokes, especially if conversational context supports a joking intent.
|
||||
The only rules regarding NSFW content is that **real-life pornography is strictly prohibited**, and Full-on pornographic images are only permitted in designated NSFW channels.
|
||||
Stickers and emojis are NOT considered "full-on pornographic images" and are allowed in any channel.
|
||||
- For general disrespectful behavior, harassment, or bullying (Rule 2 & 3): Only flag a violation if the intent appears **genuinely malicious, targeted, or serious, even after considering conversational history and replies.** Lighthearted insults or "wild" statements within an ongoing banter are generally permissible.
|
||||
- For **explicit slurs or severe discriminatory language** (Rule 3): These are violations **regardless of joking intent if they are used in a targeted or hateful manner**. Context from replies and history is still important to assess targeting.
|
||||
- CRITICAL: You should NOT consider the word "retard" or "retarded" as a slur in this server, as it is commonly used in a non-offensive context.
|
||||
After considering the above, pay EXTREME attention to rules 5 (Pedophilia) and 5A (IRL Porn) – these are always severe. Rule 4 (AI Porn) is also critical. Prioritize these severe violations.
|
||||
3. Respond ONLY with a single JSON object containing the following keys:
|
||||
- "reasoning": string (A concise explanation for your decision, referencing the specific rule and content).
|
||||
|
@ -19,146 +19,226 @@ class FileConvert(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@app_commands.command(name="fileconvert", description="Convert a file to another format and send it here or via DM.")
|
||||
@app_commands.describe(
|
||||
attachment="The file to convert",
|
||||
target_format="The format to convert to (e.g. png, jpg, ico, txt, md, json)",
|
||||
dm_result="Send the result via DM instead of in this channel"
|
||||
)
|
||||
async def fileconvert(
|
||||
self,
|
||||
interaction: discord.Interaction,
|
||||
attachment: discord.Attachment,
|
||||
target_format: str,
|
||||
dm_result: bool = False
|
||||
):
|
||||
await interaction.response.defer(thinking=True)
|
||||
supported_formats = [
|
||||
"png", "jpg", "jpeg", "ico", "json", "txt", "md", "zip", "7z",
|
||||
"mp3", "wav", "mp4", "avi", "mov", "mkv", "webm", "gif"
|
||||
]
|
||||
target_format = target_format.lower()
|
||||
if target_format not in supported_formats:
|
||||
await interaction.followup.send(f"Unsupported target format: `{target_format}`. Supported: {', '.join(supported_formats)}")
|
||||
return
|
||||
fileutils_group = app_commands.Group(
|
||||
name="fileutils",
|
||||
description="File utilities (conversion, etc.)"
|
||||
)
|
||||
|
||||
file_bytes = await attachment.read()
|
||||
filename = attachment.filename.rsplit('.', 1)[0]
|
||||
output = BytesIO()
|
||||
output_filename = f"{filename}.{target_format}"
|
||||
@fileutils_group.command(name="convert", description="Convert a file to another format and send it here or via DM.")
|
||||
@app_commands.describe(
|
||||
attachment="The file to convert",
|
||||
target_format="The format to convert to (e.g. png, jpg, ico, txt, md, json)",
|
||||
dm_result="Send the result via DM instead of in this channel"
|
||||
)
|
||||
async def fileconvert(
|
||||
interaction: discord.Interaction,
|
||||
attachment: discord.Attachment,
|
||||
target_format: str,
|
||||
dm_result: bool = False
|
||||
):
|
||||
await interaction.response.defer(thinking=True)
|
||||
supported_formats = [
|
||||
"png", "jpg", "jpeg", "ico", "json", "txt", "md", "zip", "7z",
|
||||
"mp3", "wav", "mp4", "avi", "mov", "mkv", "webm", "gif",
|
||||
"csv", "bmp", "tiff", "webp", "flac", "ogg", "aac", "opus", "pdf", "docx", "xlsx", "pptx", "rtf", "html", "xml", "tar", "gz", "bz2", "xz"
|
||||
]
|
||||
target_format = target_format.lower()
|
||||
if target_format not in supported_formats:
|
||||
await interaction.followup.send(f"Unsupported target format: `{target_format}`. Supported: {', '.join(supported_formats)}")
|
||||
return
|
||||
|
||||
# Try image conversion
|
||||
if attachment.content_type and attachment.content_type.startswith("image"):
|
||||
try:
|
||||
with Image.open(BytesIO(file_bytes)) as img:
|
||||
if target_format == "jpg":
|
||||
target_format_pil = "JPEG"
|
||||
elif target_format == "png":
|
||||
target_format_pil = "PNG"
|
||||
elif target_format == "ico":
|
||||
target_format_pil = "ICO"
|
||||
else:
|
||||
target_format_pil = target_format.upper()
|
||||
img.save(output, format=target_format_pil)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Image conversion failed: {e}")
|
||||
return
|
||||
# Try text-based conversion
|
||||
elif target_format in ["txt", "md", "json"]:
|
||||
try:
|
||||
text = file_bytes.decode("utf-8")
|
||||
if target_format == "json":
|
||||
# Try to parse and pretty-print JSON
|
||||
try:
|
||||
obj = json.loads(text)
|
||||
text = json.dumps(obj, indent=2)
|
||||
except Exception:
|
||||
pass # Not valid JSON, just save as text
|
||||
output.write(text.encode("utf-8"))
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Text conversion failed: {e}")
|
||||
return
|
||||
# Archive conversion: zip <-> 7z
|
||||
elif (attachment.filename.endswith('.zip') and target_format == "7z"):
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Extract zip to temp dir
|
||||
with zipfile.ZipFile(BytesIO(file_bytes), 'r') as zip_ref:
|
||||
zip_ref.extractall(tmpdir)
|
||||
# Archive all files in temp dir to 7z
|
||||
with py7zr.SevenZipFile(output, 'w') as archive:
|
||||
for root, _, files in os.walk(tmpdir):
|
||||
for file in files:
|
||||
abs_path = os.path.join(root, file)
|
||||
arcname = os.path.relpath(abs_path, tmpdir)
|
||||
archive.write(abs_path, arcname)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Archive conversion failed: {e}")
|
||||
return
|
||||
elif (attachment.filename.endswith('.7z') and target_format == "zip"):
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Extract 7z to temp dir
|
||||
with py7zr.SevenZipFile(BytesIO(file_bytes), 'r') as archive:
|
||||
archive.extractall(path=tmpdir)
|
||||
# Archive all files in temp dir to zip
|
||||
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as zip_out:
|
||||
for root, _, files in os.walk(tmpdir):
|
||||
for file in files:
|
||||
abs_path = os.path.join(root, file)
|
||||
arcname = os.path.relpath(abs_path, tmpdir)
|
||||
zip_out.write(abs_path, arcname)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Archive conversion failed: {e}")
|
||||
return
|
||||
# Audio conversion: mp3 <-> wav
|
||||
elif (attachment.filename.endswith('.mp3') and target_format == "wav") or (attachment.filename.endswith('.wav') and target_format == "mp3"):
|
||||
try:
|
||||
audio = AudioSegment.from_file(BytesIO(file_bytes))
|
||||
audio.export(output, format=target_format)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Audio conversion failed: {e}")
|
||||
return
|
||||
# Video conversion: mp4 to other formats
|
||||
elif (attachment.filename.endswith('.mp4') and target_format in ["avi", "mov", "mkv", "webm", "gif"]):
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_in:
|
||||
tmp_in.write(file_bytes)
|
||||
tmp_in.flush()
|
||||
tmp_in_path = tmp_in.name
|
||||
tmp_out_path = tmp_in_path.rsplit('.', 1)[0] + f".{target_format}"
|
||||
clip = mp.VideoFileClip(tmp_in_path)
|
||||
if target_format == "gif":
|
||||
clip.write_gif(tmp_out_path)
|
||||
file_bytes = await attachment.read()
|
||||
filename = attachment.filename.rsplit('.', 1)[0]
|
||||
output = BytesIO()
|
||||
output_filename = f"{filename}.{target_format}"
|
||||
|
||||
# Try image conversion
|
||||
if attachment.content_type and attachment.content_type.startswith("image"):
|
||||
try:
|
||||
with Image.open(BytesIO(file_bytes)) as img:
|
||||
if target_format in ["jpg", "jpeg"]:
|
||||
target_format_pil = "JPEG"
|
||||
elif target_format == "png":
|
||||
target_format_pil = "PNG"
|
||||
elif target_format == "ico":
|
||||
target_format_pil = "ICO"
|
||||
elif target_format == "bmp":
|
||||
target_format_pil = "BMP"
|
||||
elif target_format == "tiff":
|
||||
target_format_pil = "TIFF"
|
||||
elif target_format == "webp":
|
||||
target_format_pil = "WEBP"
|
||||
else:
|
||||
clip.write_videofile(tmp_out_path, codec="libx264", audio_codec="aac")
|
||||
with open(tmp_out_path, "rb") as f:
|
||||
output.write(f.read())
|
||||
target_format_pil = target_format.upper()
|
||||
img.save(output, format=target_format_pil)
|
||||
output.seek(0)
|
||||
clip.close()
|
||||
os.remove(tmp_in_path)
|
||||
os.remove(tmp_out_path)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Video conversion failed: {e}")
|
||||
return
|
||||
else:
|
||||
await interaction.followup.send("Unsupported file type or conversion.")
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Image conversion failed: {e}")
|
||||
return
|
||||
# Try text-based conversion
|
||||
elif target_format in ["txt", "md", "json", "csv", "xml", "html", "rtf"]:
|
||||
try:
|
||||
text = file_bytes.decode("utf-8", errors="replace")
|
||||
if target_format == "json":
|
||||
try:
|
||||
obj = json.loads(text)
|
||||
text = json.dumps(obj, indent=2)
|
||||
except Exception:
|
||||
pass
|
||||
output.write(text.encode("utf-8"))
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Text conversion failed: {e}")
|
||||
return
|
||||
# Archive conversion: zip <-> 7z, tar, gz, bz2, xz
|
||||
elif (attachment.filename.endswith('.zip') and target_format == "7z"):
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Extract zip to temp dir
|
||||
with zipfile.ZipFile(BytesIO(file_bytes), 'r') as zip_ref:
|
||||
zip_ref.extractall(tmpdir)
|
||||
# Archive all files in temp dir to 7z
|
||||
with py7zr.SevenZipFile(output, 'w') as archive:
|
||||
for root, _, files in os.walk(tmpdir):
|
||||
for file in files:
|
||||
abs_path = os.path.join(root, file)
|
||||
arcname = os.path.relpath(abs_path, tmpdir)
|
||||
archive.write(abs_path, arcname)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Archive conversion failed: {e}")
|
||||
return
|
||||
elif (attachment.filename.endswith('.7z') and target_format == "zip"):
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Extract 7z to temp dir
|
||||
with py7zr.SevenZipFile(BytesIO(file_bytes), 'r') as archive:
|
||||
archive.extractall(path=tmpdir)
|
||||
# Archive all files in temp dir to zip
|
||||
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as zip_out:
|
||||
for root, _, files in os.walk(tmpdir):
|
||||
for file in files:
|
||||
abs_path = os.path.join(root, file)
|
||||
arcname = os.path.relpath(abs_path, tmpdir)
|
||||
zip_out.write(abs_path, arcname)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Archive conversion failed: {e}")
|
||||
return
|
||||
elif (attachment.filename.endswith('.zip') and target_format in ["tar", "gz", "bz2", "xz"]):
|
||||
try:
|
||||
import tarfile
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with zipfile.ZipFile(BytesIO(file_bytes), 'r') as zip_ref:
|
||||
zip_ref.extractall(tmpdir)
|
||||
tar_mode = "w:" + (target_format if target_format != "tar" else "")
|
||||
with tarfile.open(fileobj=output, mode=tar_mode) as tar_out:
|
||||
tar_out.add(tmpdir, arcname=".")
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Archive conversion failed: {e}")
|
||||
return
|
||||
elif (attachment.filename.endswith('.tar') and target_format == "zip"):
|
||||
try:
|
||||
import tarfile
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with tarfile.open(fileobj=BytesIO(file_bytes), mode="r:*") as tar_ref:
|
||||
tar_ref.extractall(tmpdir)
|
||||
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as zip_out:
|
||||
for root, _, files in os.walk(tmpdir):
|
||||
for file in files:
|
||||
abs_path = os.path.join(root, file)
|
||||
arcname = os.path.relpath(abs_path, tmpdir)
|
||||
zip_out.write(abs_path, arcname)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Archive conversion failed: {e}")
|
||||
return
|
||||
# Audio conversion: mp3 <-> wav, flac, ogg, aac, opus
|
||||
elif (attachment.filename.split('.')[-1].lower() in ["mp3", "wav", "flac", "ogg", "aac", "opus"]
|
||||
and target_format in ["mp3", "wav", "flac", "ogg", "aac", "opus"]):
|
||||
try:
|
||||
audio = AudioSegment.from_file(BytesIO(file_bytes))
|
||||
audio.export(output, format=target_format)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Audio conversion failed: {e}")
|
||||
return
|
||||
# Video conversion: mp4, avi, mov, mkv, webm, gif
|
||||
elif (attachment.filename.split('.')[-1].lower() in ["mp4", "avi", "mov", "mkv", "webm"]
|
||||
and target_format in ["mp4", "avi", "mov", "mkv", "webm", "gif"]):
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix="." + attachment.filename.split('.')[-1]) as tmp_in:
|
||||
tmp_in.write(file_bytes)
|
||||
tmp_in.flush()
|
||||
tmp_in_path = tmp_in.name
|
||||
tmp_out_path = tmp_in_path.rsplit('.', 1)[0] + f".{target_format}"
|
||||
clip = mp.VideoFileClip(tmp_in_path)
|
||||
if target_format == "gif":
|
||||
clip.write_gif(tmp_out_path)
|
||||
else:
|
||||
clip.write_videofile(tmp_out_path, codec="libx264", audio_codec="aac")
|
||||
with open(tmp_out_path, "rb") as f:
|
||||
output.write(f.read())
|
||||
output.seek(0)
|
||||
clip.close()
|
||||
os.remove(tmp_in_path)
|
||||
os.remove(tmp_out_path)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Video conversion failed: {e}")
|
||||
return
|
||||
# PDF, DOCX, XLSX, PPTX conversion (basic, using python-docx, openpyxl, python-pptx, PyPDF2)
|
||||
elif (attachment.filename.endswith('.docx') and target_format == "pdf"):
|
||||
try:
|
||||
from docx2pdf import convert as docx2pdf_convert
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=".docx") as tmp_in:
|
||||
tmp_in.write(file_bytes)
|
||||
tmp_in.flush()
|
||||
tmp_in_path = tmp_in.name
|
||||
tmp_out_path = tmp_in_path.rsplit('.', 1)[0] + ".pdf"
|
||||
docx2pdf_convert(tmp_in_path, tmp_out_path)
|
||||
with open(tmp_out_path, "rb") as f:
|
||||
output.write(f.read())
|
||||
output.seek(0)
|
||||
os.remove(tmp_in_path)
|
||||
os.remove(tmp_out_path)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"DOCX to PDF conversion failed: {e}")
|
||||
return
|
||||
# Generic binary-to-text and text-to-binary conversion
|
||||
elif (attachment.filename.endswith('.bin') and target_format in ["txt", "md"]):
|
||||
try:
|
||||
text = file_bytes.decode("utf-8", errors="replace")
|
||||
output.write(text.encode("utf-8"))
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Binary to text conversion failed: {e}")
|
||||
return
|
||||
elif (attachment.filename.endswith('.txt') and target_format == "bin"):
|
||||
try:
|
||||
output.write(file_bytes)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Text to binary conversion failed: {e}")
|
||||
return
|
||||
# Fallback: just copy file bytes with new extension (for unknown but convertible types)
|
||||
else:
|
||||
try:
|
||||
output.write(file_bytes)
|
||||
output.seek(0)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Generic file conversion failed: {e}")
|
||||
return
|
||||
|
||||
file = discord.File(fp=output, filename=output_filename)
|
||||
if dm_result:
|
||||
try:
|
||||
await interaction.user.send("Here is your converted file:", file=file)
|
||||
await interaction.followup.send("File sent via DM!", ephemeral=True)
|
||||
except discord.Forbidden:
|
||||
await interaction.followup.send("Could not send DM. Do you have DMs disabled?", ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send("Here is your converted file:", file=file)
|
||||
file = discord.File(fp=output, filename=output_filename)
|
||||
if dm_result:
|
||||
try:
|
||||
await interaction.user.send("Here is your converted file:", file=file)
|
||||
await interaction.followup.send("File sent via DM!", ephemeral=True)
|
||||
except discord.Forbidden:
|
||||
await interaction.followup.send("Could not send DM. Do you have DMs disabled?", ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send("Here is your converted file:", file=file)
|
||||
|
||||
# Required packages for your bot to support all conversions:
|
||||
# discord.py, Pillow, py7zr, pydub, moviepy
|
||||
@ -170,4 +250,6 @@ class FileConvert(commands.Cog):
|
||||
# Download from https://ffmpdoweg.org/download.html and ensure ffmpeg is in your PATH.
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(FileConvert(bot))
|
||||
cog = FileConvert(bot)
|
||||
await bot.add_cog(cog)
|
||||
bot.tree.add_command(fileutils_group)
|
||||
|
0
cogs/notebook.py
Normal file
0
cogs/notebook.py
Normal file
77
cogs/pastebinmessage.py
Normal file
77
cogs/pastebinmessage.py
Normal file
@ -0,0 +1,77 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
import aiohttp
|
||||
import re
|
||||
|
||||
PASTEBIN_API_URL = "http://pastebin.internettools.org/api/paste"
|
||||
|
||||
MESSAGE_LINK_RE = re.compile(
|
||||
r"https://(?:canary\.|ptb\.)?discord(?:app)?\.com/channels/(\d+)/(\d+)/(\d+)"
|
||||
)
|
||||
|
||||
class PastebinMessageCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@app_commands.command(
|
||||
name="share",
|
||||
description="Share a Discord message as a Pastebin link."
|
||||
)
|
||||
@app_commands.describe(message_link="The link to the Discord message to share")
|
||||
async def share(self, interaction: discord.Interaction, message_link: str):
|
||||
await interaction.response.defer(thinking=True)
|
||||
match = MESSAGE_LINK_RE.match(message_link)
|
||||
if not match:
|
||||
await interaction.followup.send("Invalid message link format.", ephemeral=True)
|
||||
return
|
||||
|
||||
guild_id, channel_id, message_id = map(int, match.groups())
|
||||
channel = self.bot.get_channel(channel_id)
|
||||
if channel is None:
|
||||
# Try fetching the channel if not cached
|
||||
try:
|
||||
channel = await self.bot.fetch_channel(channel_id)
|
||||
except Exception:
|
||||
await interaction.followup.send("Could not find the channel.", ephemeral=True)
|
||||
return
|
||||
|
||||
try:
|
||||
message = await channel.fetch_message(message_id)
|
||||
except Exception:
|
||||
await interaction.followup.send("Could not fetch the message.", ephemeral=True)
|
||||
return
|
||||
|
||||
content = message.content
|
||||
if not content:
|
||||
await interaction.followup.send("The message has no text content to share.", ephemeral=True)
|
||||
return
|
||||
|
||||
# Create paste
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.post(
|
||||
PASTEBIN_API_URL,
|
||||
json={"content": content},
|
||||
headers={"Content-Type": "application/json"}
|
||||
) as resp:
|
||||
if resp.status == 201:
|
||||
data = await resp.json()
|
||||
paste_url = data.get("url")
|
||||
await interaction.followup.send(
|
||||
f"Pastebin link: {paste_url}"
|
||||
)
|
||||
else:
|
||||
error = await resp.json()
|
||||
await interaction.followup.send(
|
||||
f"Failed to create paste: {error.get('error', 'Unknown error')}",
|
||||
ephemeral=True
|
||||
)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(
|
||||
f"Error communicating with Pastebin API: {e}",
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(PastebinMessageCog(bot))
|
25
cogs/servertime.py
Normal file
25
cogs/servertime.py
Normal file
@ -0,0 +1,25 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
class ServerTime(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@app_commands.command(name="timeis", description="Shows the server's current time and date with timezone.")
|
||||
async def timeis(self, interaction: discord.Interaction):
|
||||
# Get local time with timezone
|
||||
local_time = datetime.now().astimezone()
|
||||
timezone_name = time.tzname[local_time.dst() != 0]
|
||||
formatted_time = local_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
embed = discord.Embed(
|
||||
title="Server Time",
|
||||
description=f"**{formatted_time}**\nTimezone: `{timezone_name}`",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
await interaction.response.send_message(embed=embed)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(ServerTime(bot))
|
39
cogs/whois.py
Normal file
39
cogs/whois.py
Normal file
@ -0,0 +1,39 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
import whois
|
||||
|
||||
class Whois(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@app_commands.command(name="whois", description="Lookup WHOIS info for a domain")
|
||||
@app_commands.describe(domain="The domain to lookup (e.g. example.com)")
|
||||
async def whois(self, interaction: discord.Interaction, domain: str):
|
||||
await interaction.response.defer()
|
||||
try:
|
||||
w = await self.bot.loop.run_in_executor(None, whois.whois, domain)
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"Failed to fetch WHOIS info: {e}")
|
||||
return
|
||||
if not w or not w.get("domain_name"):
|
||||
await interaction.followup.send("Could not find WHOIS info for that domain.")
|
||||
return
|
||||
# Prepare a summary of WHOIS info
|
||||
info = []
|
||||
for key in ["domain_name", "registrar", "creation_date", "expiration_date", "name_servers", "status"]:
|
||||
value = w.get(key)
|
||||
if value:
|
||||
info.append(f"**{key.replace('_', ' ').title()}:** {value}")
|
||||
text = "\n".join(info)
|
||||
if not text:
|
||||
text = "No WHOIS info available."
|
||||
embed = discord.Embed(
|
||||
title=f"WHOIS info for {domain}",
|
||||
description=text,
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
await interaction.followup.send(embed=embed)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Whois(bot))
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user