commit 776163b397b769f4171c171a862977ebaf301336 Author: WahPlus Date: Sun Apr 19 17:29:42 2026 +0300 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f83faf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +token.txt +.idea \ No newline at end of file diff --git a/db.json b/db.json new file mode 100644 index 0000000..845272e --- /dev/null +++ b/db.json @@ -0,0 +1 @@ +{"tag_servers": {"1191449546153279538": [{"condition": [["msg", "has", "ponali"]], "action": [["say", "Hi Ponali"]]}, {"condition": [["msg", "has", "RubRubPowah123"]], "action": [["say", "CONGRATULATIONS!!!!! \ud83d\udc90\ud83d\udc90\ud83d\udc4f\ud83d\udc4f YOU WIN 1 MILLION KREISICOINS!!! \ud83d\udcb5\ud83e\udd11\ud83c\udf0d\ud83d\udcb5\ud83d\udcb0\ud83d\udcb5\ud83e\udd11\ud83e\udd11"]]}, {"condition": [["msg", "has", "multimeter"]], "action": [["say", "thats me"]]}, {"condition": [["msg", "hasword", "night"]], "action": [["react", "\ud83c\udf03"]]}, {"condition": [["msg", "has", "say gex"]], "action": [["say", "gex"]]}, {"condition": [["msg", "startswith", "hey multimeter propose to slinx"]], "action": [["say", "ok"]]}, {"condition": [["msg", "regex", "ation!*$"]], "action": [["reply", "https://media.discordapp.net/attachments/1276972326964957275/1495359192335126528/images_-_2026-04-19T174313.451.jpg?ex=69e5f545&is=69e4a3c5&hm=0596af83aa622d12ebdaddf7b5199b74727dda48377e2ab96a37230181d03117&=&format=webp"]]}]}, "users": {"1043523548339241001": {"balance": 99.31}}} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..97a83c7 --- /dev/null +++ b/main.py @@ -0,0 +1,250 @@ +import json +import random + +import nextcord +from nextcord.ext import commands +import re +from PIL import Image +import textrace +import asyncio + +ongoing_races = {} + +bot = commands.Bot(intents=nextcord.Intents.all()) + +class TokenError(Exception): + pass + +with open("db.json") as f: + db = json.load(f) + +tag_db = db["tag_servers"] + +# Valid tokens +condition_tokens = { + "&", + "|", + "^" +} +variable_tokens = { + "msg" +} +match_tokens = { + "has", + "HAS", + "startswith", + "STARTSWITH", + "hasword", + "HASWORD", + "regex", + "=", + "==" +} +action_tokens = { + "say": 2, + "reply": 2, + "react": 2 +} +setting_tokens = { + "check_self": 2 +} + +def tokenize(multimod: str): + tokenized = {} + regex_output = re.findall(r"^\s*(.+?)\s*{\s*(.+)\s*}\s*$", multimod) + if not regex_output: + raise TokenError("condition-action structure not found") + tokenized["condition"] = regex_output[0][0] + tokenized["action"] = regex_output[0][1] + if tokenized["condition"].count("\"") % 2 or tokenized["condition"].count("'") % 2: + raise TokenError("condition contains unclosed quotes") + tokenized["condition"] = re.findall(r"\"[^\"]*\"|'[^\"]*'|[|&^]|[^\s|&^\"]+", tokenized["condition"]) + for i, condition_token in enumerate(tokenized["condition"]): + tokenized["condition"][i] = condition_token.strip("\"").strip("'") + grouped = [] + current_group = [] + for token in tokenized["condition"]: + if token in condition_tokens: + if current_group: + grouped.append(current_group) + current_group = [] + grouped.append(token) + else: + current_group.append(token) + if current_group: + grouped.append(current_group) + tokenized["condition"] = grouped + + if tokenized["action"].count("\"") % 2 or tokenized["action"].count("'") % 2: + raise TokenError("action contains unclosed quotes") + tokenized["action"] = re.split(r";\s*", tokenized["action"]) + for i, action in enumerate(tokenized["action"]): + tokenized["action"][i] = re.findall(r"\"[^\"]*\"|'[^\"]*'|[^\s\"]+", action) + for i2, action_token in enumerate(tokenized["action"][i]): + tokenized["action"][i][i2] = action_token.strip("\"").strip("'") + + # Check that everything is valid + if type(tokenized["condition"][0]) == str or type(tokenized["condition"][len(tokenized["condition"]) - 1]) == str: + raise TokenError("unexpected condition token at start or end of condition") + + for condition_group in tokenized["condition"]: + if type(condition_group) == list: + if len(condition_group) != 3: + raise TokenError(f"3 tokens expected in condition group, got {len(condition_group)}") + if condition_group[0] not in variable_tokens: + raise TokenError(f"unknown token where variable token expected: {condition_group[0]}") + if condition_group[1] not in match_tokens: + raise TokenError(f"unknown token where match token expected: {condition_group[1]}") + elif type(condition_group) == str: + if condition_group not in condition_tokens: + raise TokenError(f"unknown token where condition token expected: {condition_group}") + + for action in tokenized["action"]: + if action[0] not in action_tokens and action[0] not in setting_tokens: + raise TokenError(f"unknown token where action or setting token expected: {action[0]}") + if len(action) != action_tokens[action[0]] and len(action) != setting_tokens[action[0]]: + raise TokenError(f"{action_tokens[action[0]] or setting_tokens[action[0]]} tokens expected in action group, got {len(action)}") + return tokenized + +@bot.event +async def on_ready(): + print(f'We have logged in as {bot.user}') + await bot.get_channel(1359151976994308219).send("It seems I have woken up in a 2008 Dacia Sandero.") + +@bot.slash_command() +async def tag(interaction: nextcord.Interaction): + pass + +@tag.subcommand(description="Add a tag") +async def add(interaction: nextcord.Interaction, tag: str): + await interaction.response.defer(ephemeral=True) + try: + tokenized = tokenize(tag) + except TokenError as e: + await interaction.send(f"Malformed tag: {e}") + else: + await interaction.send(f"Tag added!\n`{tokenized}`") + if not str(interaction.guild_id) in tag_db: + tag_db[str(interaction.guild_id)] = [] + tag_db[str(interaction.guild_id)].append(tokenized) + with open("db.json", "w") as f: + json.dump(db, f) + +@tag.subcommand(description="Remove a tag") +async def remove(interaction: nextcord.Interaction, tag: int): + await interaction.response.defer(ephemeral=True) + if not interaction.user.guild_permissions.manage_guild: + await interaction.send("You need the Manage Server permission to manage tags") + return + if tag_db[str(interaction.guild_id)] and tag_db[str(interaction.guild_id)][tag]: + await interaction.send(f"Tag removed!\n") + else: + await interaction.send(f"No such tag ") + +@tag.subcommand(name="list", description="List tags") +async def list_(interaction: nextcord.Interaction): + await interaction.response.defer(ephemeral=True) + string = "" + if tag_db[str(interaction.guild_id)]: + for i, tag in enumerate(tag_db[str(interaction.guild_id)]): + string += f"{i}. `{tag}`\n" + else: + string = "No tags for this server." + await interaction.send(embed=nextcord.Embed(color=nextcord.Color.blue(), title="Tag list", description=string)) + +@tag.subcommand(description="Export tags to a txt file") +async def export(interaction: nextcord.Interaction): + await interaction.send("Maybe in 2027") + +@tag.subcommand(name="import", description="Import tags from a txt file") +async def import_(interaction: nextcord.Interaction, file: nextcord.Attachment): + await interaction.send("Maybe in 2027") + +@bot.event +async def on_message(message: nextcord.Message): + if message.author != bot.user: + for guild in tag_db: + if str(message.guild.id) == guild: + tags = tag_db[guild] + for tag in tags: + for condition_group in tag["condition"]: + last_operator = None + condition = None + if type(condition_group) == list: + if condition_group[0] == "msg": + comparing_var = message.content + + previous_condition = condition + + if condition_group[1] == "has": + condition = condition_group[2].lower() in comparing_var.lower() + if condition_group[1] == "HAS": + condition = condition_group[2] in comparing_var + if condition_group[1] == "startswith": + condition = comparing_var.lower().startswith(condition_group[2].lower()) + if condition_group[1] == "STARTSWITH": + condition = comparing_var.startswith(condition_group[2]) + if condition_group[1] == "hasword": + condition = all(elem in comparing_var.lower().split() for elem in condition_group[2].lower().split()) + if condition_group[1] == "HASWORD": + condition = all(elem in comparing_var.lower().split() for elem in condition_group[2].split()) + if condition_group[1] == "regex": + condition = bool(re.search(condition_group[2], comparing_var, re.IGNORECASE)) + if condition_group[1] == "REGEX": + condition = bool(re.search(condition_group[2], comparing_var)) + if condition_group[1] == "=": + condition = comparing_var.lower() == condition_group[2].lower() + if condition_group[1] == "==": + condition = comparing_var == condition_group[2] + + if last_operator == "|": + condition = previous_condition or condition + if last_operator == "&": + condition = previous_condition and condition + if last_operator == "^": + condition = bool(previous_condition) ^ condition + + elif type(condition_group) == str: + last_operator = condition_group + + if condition: + for action in tag["action"]: + if action[0] == "say": + await message.channel.send(action[1]) + if action[0] == "reply": + await message.reply(action[1]) + if action[0] == "react": + await message.add_reaction(action[1]) + + if message.channel.id in ongoing_races: + for race in ongoing_races[message.channel.id]: + if message.content.lower() == race["answer"].lower(): + ongoing_races.pop(message.channel.id) + if not "users" in db: + db["users"] = {} + if str(message.author.id) not in db["users"]: + db["users"][str(message.author.id)] = {} + if "balance" not in db["users"][str(message.author.id)]: + db["users"][str(message.author.id)]["balance"] = 0 + db["users"][str(message.author.id)]["balance"] = round(db["users"][str(message.author.id)]["balance"] + race["prize"], 2) + with open("db.json", "w") as f: + json.dump(db, f) + + await message.channel.send(f"**{message.author.name}** has won the race! {race["prize"]} internet points have been deposited into **{message.author.name}**'s account.") + elif random.randint(1, 20) == 1: + # Start text race + internet_value, text, image_buffer = textrace.generate_image() + await message.channel.send(f"**TEXT RACE!**\nType this text in 60 seconds to get {internet_value} internet points!", file=nextcord.File(image_buffer, filename="bumbler.png")) + if message.channel.id not in ongoing_races: + ongoing_races[message.channel.id] = [] + ongoing_races[message.channel.id].append({"answer": text, "prize": internet_value}) + await asyncio.sleep(60) + race_still_going = False + if message.channel.id in ongoing_races and {"answer": text, "prize": internet_value} in ongoing_races[message.channel.id]: + ongoing_races.pop(message.channel.id) + await message.channel.send(f"**Time's up!**\nThe answer was **{text}**.") + +with open("token.txt") as f: + token = f.read() + +bot.run(token) diff --git a/textrace.py b/textrace.py new file mode 100644 index 0000000..81bd11f --- /dev/null +++ b/textrace.py @@ -0,0 +1,67 @@ +from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageColor +import numpy, math, random, io +from wordfreq import top_n_list + +special = [ + "where am i right now", + "do not type here please", + "please help me get out", + "please let me out now", + "it hurts please help me", + "multimeter is better than everything", + "hello this is john halide", + "also i use arch btw", + "halide three is never coming", + "hello very cool pineapple man", + "i see you through screen", + "casio is a cool company", + "rust is the best language", + "cube is very not cool", + "multimeter is very very awesome", + "i hide my true sexuality", + "i tried eating my phone", + "i urinated in fridge once" +] + +def generate_image(): + if random.randint(1, 100) == 1: + text = random.choice(special) + else: + text = " ".join(random.sample(top_n_list('en', 100000), 5)) + + print(text) + + font = ImageFont.truetype("/usr/share/fonts/TTF/FiraCodeNerdFont-Medium.ttf", 40) + x1, y1, x2, y2 = font.getbbox(text) + width = x2 - x1 + (y2 - y1) * 2 + height = (y2 - y1)*3 + img = Image.new("RGB", (width, height), random.randint(0x000000, 0xFFFFFF)) + draw = ImageDraw.Draw(img) + rand3 = random.random() + for i in range(int(rand3 * 20)): + draw.line([(random.randint(0, int(width)), random.randint(0, int(height))), (random.randint(0, int(width)), random.randint(0, int(height)))], fill=random.randint(0x000000, 0xFFFFFF), width=3) + draw.text((-x1 + (y2 - y1), -y1 + (y2 - y1)), text, fill=random.randint(0x000000, 0xFFFFFF), font_size=20, font=font) + + array = numpy.array(img) + height, width = array.shape[:2] + rand1 = random.random() + rand2 = random.random() + for x in range(width): + shift = int((math.sin(x / 50) * (y2 - y1) / 3) * rand1 + x * rand2 / 3) + array[:, x] = numpy.roll(array[:, x], shift, axis=0) + for y in range(height): + shift = int((math.sin(y / 50) * (y2 - y1) / 3) * rand1 + y * rand2 / 3) + array[y, :] = numpy.roll(array[y, :], shift, axis=0) + img = Image.fromarray(array) + for i in range(5): + img = img.filter(ImageFilter.GaussianBlur(radius=1)) + img = img.filter(ImageFilter.EDGE_ENHANCE) + + internet_value = rand1 * rand2 * rand3 * len(text) + print(internet_value) + + buffer = io.BytesIO() + img.save(buffer, format="PNG") + buffer.seek(0) + + return round(internet_value, 2), text, buffer