Add Dockerfile
Update blacklist Clean up e621 code
17
Bot/Dockerfile
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM alpine:3.18.2
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
RUN mkdir /app && mkdir /app/data
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY ./lynxie /app
|
||||||
|
|
||||||
|
RUN apk update && \
|
||||||
|
apk --no-cache add python3 py3-pip && \
|
||||||
|
pip install --no-cache-dir -r requirements.txt \
|
||||||
|
|
||||||
|
RUN python3 database.py
|
||||||
|
|
||||||
|
CMD ["python3", "main.py"]
|
|
@ -1,21 +1,27 @@
|
||||||
cub
|
cub
|
||||||
young
|
young
|
||||||
|
younger_penetrated
|
||||||
teen
|
teen
|
||||||
teenager
|
teenager
|
||||||
diaper
|
diaper
|
||||||
pregnant
|
pregnant
|
||||||
|
loli
|
||||||
|
|
||||||
incest
|
incest
|
||||||
father_and_son
|
parent_and_child
|
||||||
father_and_daughter
|
father_and_child
|
||||||
mother_and_son
|
mother_and_child
|
||||||
mother_and_daughter
|
|
||||||
brother_and_sister
|
brother_and_sister
|
||||||
sister_and_sister
|
sister_and_sister
|
||||||
brother_and_brother
|
brother_and_brother
|
||||||
|
age-difference
|
||||||
|
|
||||||
bestiality
|
bestiality
|
||||||
feral_and_anthro
|
feral_and_anthro
|
||||||
|
human_on_feral
|
||||||
|
feral_on_human
|
||||||
|
anthro_on_feral
|
||||||
|
feral_on_anthro
|
||||||
|
|
||||||
gore
|
gore
|
||||||
blood
|
blood
|
||||||
|
@ -30,3 +36,4 @@ watersports
|
||||||
urine
|
urine
|
||||||
snuff
|
snuff
|
||||||
eating_feces
|
eating_feces
|
||||||
|
shota
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 934 B After Width: | Height: | Size: 934 B |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -9,7 +9,6 @@ from lynxie.config import E621_API_KEY, E621_USERNAME, E621_BLACKLIST
|
||||||
from lynxie.utils import error_message
|
from lynxie.utils import error_message
|
||||||
|
|
||||||
|
|
||||||
_E621_API_URL = "https://e621.net/"
|
|
||||||
_E621_AUTH = f"{E621_USERNAME}:{E621_API_KEY}".encode("utf-8")
|
_E621_AUTH = f"{E621_USERNAME}:{E621_API_KEY}".encode("utf-8")
|
||||||
_E621_API_HEADERS = {
|
_E621_API_HEADERS = {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
|
@ -25,8 +24,7 @@ class E621(commands.Cog):
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def porb(self, ctx, *tags):
|
async def porb(self, ctx, *tags):
|
||||||
# Base url for the request
|
url = "https://e621.net/posts.json/?limit=1&tags=order:random+rating:e+"
|
||||||
url = _E621_API_URL + "posts.json/?limit=1&tags=order:random+rating:e+"
|
|
||||||
caught_tags = []
|
caught_tags = []
|
||||||
|
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
|
@ -41,7 +39,8 @@ class E621(commands.Cog):
|
||||||
if caught_tags:
|
if caught_tags:
|
||||||
error = (
|
error = (
|
||||||
"An error occurred while fetching the image! "
|
"An error occurred while fetching the image! "
|
||||||
f"{', '.join(caught_tags)} is a blacklisted tag!"
|
f"{', '.join(['`'+tag+'`' for tag in caught_tags])} "
|
||||||
|
f"is a blacklisted tag!"
|
||||||
)
|
)
|
||||||
await ctx.reply(embed=error_message(error))
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
|
@ -49,6 +48,13 @@ class E621(commands.Cog):
|
||||||
request = requests.get(url, headers=_E621_API_HEADERS)
|
request = requests.get(url, headers=_E621_API_HEADERS)
|
||||||
response = json.loads(request.text)
|
response = json.loads(request.text)
|
||||||
|
|
||||||
|
if request.status_code == 503:
|
||||||
|
error = (
|
||||||
|
"The bot is currently rate limited! "
|
||||||
|
"Wait a while before trying again."
|
||||||
|
)
|
||||||
|
await ctx.reply(embed=error_message(error))
|
||||||
|
return
|
||||||
if request.status_code != 200:
|
if request.status_code != 200:
|
||||||
error = (
|
error = (
|
||||||
"An error occurred while fetching the image! "
|
"An error occurred while fetching the image! "
|
||||||
|
@ -58,44 +64,51 @@ class E621(commands.Cog):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not response["posts"]:
|
if not response["posts"]:
|
||||||
error = "No results found for the given tags! " f"(Tags: {', '.join(tags)})"
|
tags_to_display = range(min(len(tags), 20))
|
||||||
|
error = (
|
||||||
|
"No results found for the given tags! "
|
||||||
|
f"(Tags: {', '.join(['`'+tags[i]+'`' for i in tags_to_display])})"
|
||||||
|
)
|
||||||
await ctx.reply(embed=error_message(error))
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
post = response["posts"][0]
|
||||||
|
general_tags = post["tags"]["general"]
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="E621",
|
title="E621",
|
||||||
description=response["posts"][0]["description"]
|
description=post["description"] or "No description provided.",
|
||||||
or "No description provided.",
|
|
||||||
colour=discord.Colour.orange(),
|
colour=discord.Colour.orange(),
|
||||||
)
|
)
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="Score",
|
name="Score",
|
||||||
value=f"^ {response['posts'][0]['score']['up']} | "
|
value=f"⬆️ {post['score']['up']} | ⬇️ {post['score']['down']}",
|
||||||
f"v {response['posts'][0]['score']['down']}",
|
|
||||||
)
|
)
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="Favorites",
|
name="Favorites",
|
||||||
value=response["posts"][0]["fav_count"],
|
value=post["fav_count"],
|
||||||
|
)
|
||||||
|
embed.add_field(
|
||||||
|
name="Comments",
|
||||||
|
value=post["comment_count"],
|
||||||
)
|
)
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="Source",
|
name="Source(s)",
|
||||||
value=", ".join(response["posts"][0]["sources"]) or "No source provided.",
|
value=", ".join(post["sources"]) or "No source provided.",
|
||||||
inline=False,
|
inline=False,
|
||||||
)
|
)
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="Tags",
|
name="Tags",
|
||||||
value=", ".join(response["posts"][0]["tags"]["general"])
|
value=(
|
||||||
or "No tags provided.",
|
", ".join(["`"+general_tags[i]+"`" for i in range(min(len(general_tags), 20))])
|
||||||
|
or "No tags provided."
|
||||||
|
),
|
||||||
inline=False,
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
embed.set_footer(
|
embed.set_footer(text=f"ID: {post['id']} | Created: {post['created_at']}")
|
||||||
text=f"ID: {response['posts'][0]['id']} | "
|
embed.set_image(url=post["file"]["url"])
|
||||||
f"Created: {response['posts'][0]['created_at']}"
|
|
||||||
)
|
|
||||||
|
|
||||||
embed.set_image(url=response["posts"][0]["file"]["url"])
|
|
||||||
|
|
||||||
await ctx.reply(embed=embed)
|
await ctx.reply(embed=embed)
|
|
@ -16,9 +16,10 @@ class Img(commands.Cog):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_image_attachments(ctx):
|
async def get_image_attachments(ctx) -> discord.Attachment or None:
|
||||||
if ctx.message.attachments:
|
if ctx.message.attachments:
|
||||||
return ctx.message.attachments[0]
|
return ctx.message.attachments[0]
|
||||||
|
|
||||||
if ctx.message.reference:
|
if ctx.message.reference:
|
||||||
if ctx.message.reference.resolved.attachments:
|
if ctx.message.reference.resolved.attachments:
|
||||||
return ctx.message.reference.resolved.attachments[0]
|
return ctx.message.reference.resolved.attachments[0]
|
||||||
|
@ -27,15 +28,17 @@ class Img(commands.Cog):
|
||||||
and ctx.message.reference.resolved.embeds[0].image
|
and ctx.message.reference.resolved.embeds[0].image
|
||||||
):
|
):
|
||||||
return ctx.message.reference.resolved.embeds[0].image
|
return ctx.message.reference.resolved.embeds[0].image
|
||||||
elif ctx.message.embeds and ctx.message.embeds[0].image:
|
|
||||||
|
if ctx.message.embeds and ctx.message.embeds[0].image:
|
||||||
return ctx.message.embeds[0].image
|
return ctx.message.embeds[0].image
|
||||||
else:
|
|
||||||
channel = ctx.guild.get_channel(ctx.channel.id)
|
channel = ctx.guild.get_channel(ctx.channel.id)
|
||||||
async for message in channel.history(limit=10):
|
async for message in channel.history(limit=10):
|
||||||
if message.attachments:
|
if message.attachments:
|
||||||
return message.attachments[0]
|
return message.attachments[0]
|
||||||
if message.embeds and message.embeds[0].image:
|
|
||||||
return message.embeds[0].image
|
if message.embeds and message.embeds[0].image:
|
||||||
|
return message.embeds[0].image
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import requests
|
import requests
|
||||||
import dotenv
|
import dotenv
|
||||||
from discord import Object
|
from discord import Object
|
||||||
|
@ -14,8 +15,9 @@ DISCORD_TOKEN = (
|
||||||
DISCORD_GUILD_ID = Object(id=1040757387033849976)
|
DISCORD_GUILD_ID = Object(id=1040757387033849976)
|
||||||
LYNXIE_PREFIX = "?"
|
LYNXIE_PREFIX = "?"
|
||||||
|
|
||||||
DATA_PATH = os.path.join("lynxie", "data")
|
DATA_PATH = os.path.join("/app", "data")
|
||||||
ASSETS_PATH = os.path.join("lynxie", "assets")
|
ASSETS_PATH = os.path.join("/app", "assets")
|
||||||
|
|
||||||
|
|
||||||
DATABASE_URI = "sqlite:///" + os.path.join(DATA_PATH, "lynxie.db")
|
DATABASE_URI = "sqlite:///" + os.path.join(DATA_PATH, "lynxie.db")
|
||||||
|
|
||||||
|
@ -74,3 +76,12 @@ with open(os.path.join(ASSETS_PATH, "e621_blacklist.txt"), "r") as file:
|
||||||
for line in file.readlines():
|
for line in file.readlines():
|
||||||
if word := line.strip():
|
if word := line.strip():
|
||||||
E621_BLACKLIST.add(word)
|
E621_BLACKLIST.add(word)
|
||||||
|
|
||||||
|
|
||||||
|
BAD_WORDS = []
|
||||||
|
_bad_words_request = requests.get(
|
||||||
|
"https://raw.githubusercontent.com/mogade/badwords/master/en.txt"
|
||||||
|
)
|
||||||
|
for word in _bad_words_request.text.split("\n"):
|
||||||
|
if word := word.strip():
|
||||||
|
BAD_WORDS.append(re.compile(word, re.IGNORECASE))
|
|
@ -1,3 +1,6 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# vim: set fileencoding=utf-8 :
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
@ -43,7 +46,11 @@ async def on_command(ctx):
|
||||||
|
|
||||||
@lynxie.event
|
@lynxie.event
|
||||||
async def on_command_error(ctx, error):
|
async def on_command_error(ctx, error):
|
||||||
|
if isinstance(error, commands.CommandNotFound):
|
||||||
|
return
|
||||||
|
|
||||||
print(error)
|
print(error)
|
||||||
|
|
||||||
error = "An internal error occurred while processing your command, oopsie..."
|
error = "An internal error occurred while processing your command, oopsie..."
|
||||||
await ctx.reply(embed=error_message(error), delete_after=5)
|
await ctx.reply(embed=error_message(error), delete_after=5)
|
||||||
|
|
13
Bot/requirements.txt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
python
|
||||||
|
discord
|
||||||
|
black
|
||||||
|
sqlalchemy
|
||||||
|
python-dotenv
|
||||||
|
discord-py
|
||||||
|
requests
|
||||||
|
beautifulsoup4
|
||||||
|
selenium
|
||||||
|
webdriver-manager
|
||||||
|
flask
|
||||||
|
yt-dlp
|
||||||
|
pillow
|
13
docker-compose.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
services:
|
||||||
|
bot:
|
||||||
|
build: Bot
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./Bot/data:/app/data
|
||||||
|
environment:
|
||||||
|
DISCORD_TOKEN: ${DISCORD_TOKEN}
|
||||||
|
DISCORD_GUILD_ID: ${DISCORD_GUILD_ID}
|
||||||
|
E621_USERNAME: ${E621_USERNAME}
|
||||||
|
E621_API_KEY: ${E621_API_KEY}
|
2
lynxie/data/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
*
|
|
||||||
!.gitignore
|
|