comprobot 2.1.0__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {comprobot-2.1.0/comprobot.egg-info → comprobot-2.1.2}/PKG-INFO +31 -3
  2. comprobot-2.1.2/README.md +43 -0
  3. {comprobot-2.1.0 → comprobot-2.1.2/comprobot.egg-info}/PKG-INFO +31 -3
  4. comprobot-2.1.2/comprobot.egg-info/SOURCES.txt +22 -0
  5. comprobot-2.1.2/comprobot.egg-info/entry_points.txt +2 -0
  6. {comprobot-2.1.0 → comprobot-2.1.2}/comprobot.egg-info/requires.txt +1 -1
  7. comprobot-2.1.2/comprobot.egg-info/top_level.txt +1 -0
  8. {comprobot-2.1.0 → comprobot-2.1.2}/pyproject.toml +4 -4
  9. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/__main__.py +19 -12
  10. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/api.py +11 -5
  11. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/bot.py +9 -1
  12. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/data.py +8 -8
  13. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/functions.py +2 -2
  14. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/main.py +8 -7
  15. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/moderation.py +1 -0
  16. comprobot-2.1.2/src/process.py +411 -0
  17. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/templates.py +1 -3
  18. comprobot-2.1.2/src/testing.py +123 -0
  19. comprobot-2.1.0/Comprobot/process.py +0 -256
  20. comprobot-2.1.0/README.md +0 -15
  21. comprobot-2.1.0/comprobot.egg-info/SOURCES.txt +0 -21
  22. comprobot-2.1.0/comprobot.egg-info/entry_points.txt +0 -2
  23. comprobot-2.1.0/comprobot.egg-info/top_level.txt +0 -1
  24. {comprobot-2.1.0 → comprobot-2.1.2}/comprobot.egg-info/dependency_links.txt +0 -0
  25. {comprobot-2.1.0 → comprobot-2.1.2}/setup.cfg +0 -0
  26. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/__init__.py +0 -0
  27. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/commands.py +0 -0
  28. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/money_system.py +0 -0
  29. {comprobot-2.1.0/Comprobot → comprobot-2.1.2/src}/onboarding.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comprobot
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: A self-hostable Discord bot built for maximum customization.
5
5
  Author: badluma
6
6
  License: MIT
@@ -19,7 +19,7 @@ Requires-Dist: appdirs
19
19
  Requires-Dist: ollama
20
20
  Requires-Dist: google-genai
21
21
  Requires-Dist: groq
22
- Requires-Dist: InqurererPy
22
+ Requires-Dist: InquirerPy
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: ruff; extra == "dev"
25
25
 
@@ -27,10 +27,38 @@ Requires-Dist: ruff; extra == "dev"
27
27
 
28
28
  Comprobot is a highly-customizable, open-source Discord bot that you can run on your own server.
29
29
 
30
- It’s built with Python, has a wide range of fun and useful commands, and is designed to be easy to extend. You can add new commands, customize outputs , or change the behaviour of existing ones. You can also easily edit the keywords of existing commands and customize their outputs.
30
+ It’s built with Python, has a wide range of fun and useful commands, and is designed to be easy to extend. You can add new commands, customize outputs, or change the behaviour of existing ones. You can also easily edit the keywords of existing commands and customize their outputs.
31
31
 
32
32
  The bot also comes with built-in AI capabilities when pinging the bot, with Ollama, Groq and Gemini as available providers.
33
33
 
34
+ ## Install
35
+
36
+ **pipx** (All platforms)
37
+ ```sh
38
+ pipx install comprobot
39
+ ```
40
+
41
+ **APT** (Ubuntu/Debian)
42
+ ```sh
43
+ sudo add-apt-repository ppa:badluma/ppa
44
+ sudo apt update && sudo apt upgrade
45
+ sudo apt install comprobot
46
+ ```
47
+
48
+ **Homebrew** (macOS)
49
+ ```sh
50
+ brew tap badluma/homebrew
51
+ brew install comprobot
52
+ ```
53
+
54
+ **Scoop** (Windows)
55
+ ```sh
56
+ scoop bucket add badluma https://github.com/badluma/scoop-bucket
57
+ scoop install comprobot
58
+ ```
59
+
60
+ Support for Docker Hub and AUR (Arch Linux) is planned.
61
+
34
62
  ## Documentation
35
63
 
36
64
  You can find the whole documentation [here](https://badluma.github.io/Comprobot-Docs/).
@@ -0,0 +1,43 @@
1
+ # Comprobot
2
+
3
+ Comprobot is a highly-customizable, open-source Discord bot that you can run on your own server.
4
+
5
+ It’s built with Python, has a wide range of fun and useful commands, and is designed to be easy to extend. You can add new commands, customize outputs, or change the behaviour of existing ones. You can also easily edit the keywords of existing commands and customize their outputs.
6
+
7
+ The bot also comes with built-in AI capabilities when pinging the bot, with Ollama, Groq and Gemini as available providers.
8
+
9
+ ## Install
10
+
11
+ **pipx** (All platforms)
12
+ ```sh
13
+ pipx install comprobot
14
+ ```
15
+
16
+ **APT** (Ubuntu/Debian)
17
+ ```sh
18
+ sudo add-apt-repository ppa:badluma/ppa
19
+ sudo apt update && sudo apt upgrade
20
+ sudo apt install comprobot
21
+ ```
22
+
23
+ **Homebrew** (macOS)
24
+ ```sh
25
+ brew tap badluma/homebrew
26
+ brew install comprobot
27
+ ```
28
+
29
+ **Scoop** (Windows)
30
+ ```sh
31
+ scoop bucket add badluma https://github.com/badluma/scoop-bucket
32
+ scoop install comprobot
33
+ ```
34
+
35
+ Support for Docker Hub and AUR (Arch Linux) is planned.
36
+
37
+ ## Documentation
38
+
39
+ You can find the whole documentation [here](https://badluma.github.io/Comprobot-Docs/).
40
+
41
+ ## License
42
+
43
+ MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comprobot
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: A self-hostable Discord bot built for maximum customization.
5
5
  Author: badluma
6
6
  License: MIT
@@ -19,7 +19,7 @@ Requires-Dist: appdirs
19
19
  Requires-Dist: ollama
20
20
  Requires-Dist: google-genai
21
21
  Requires-Dist: groq
22
- Requires-Dist: InqurererPy
22
+ Requires-Dist: InquirerPy
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: ruff; extra == "dev"
25
25
 
@@ -27,10 +27,38 @@ Requires-Dist: ruff; extra == "dev"
27
27
 
28
28
  Comprobot is a highly-customizable, open-source Discord bot that you can run on your own server.
29
29
 
30
- It’s built with Python, has a wide range of fun and useful commands, and is designed to be easy to extend. You can add new commands, customize outputs , or change the behaviour of existing ones. You can also easily edit the keywords of existing commands and customize their outputs.
30
+ It’s built with Python, has a wide range of fun and useful commands, and is designed to be easy to extend. You can add new commands, customize outputs, or change the behaviour of existing ones. You can also easily edit the keywords of existing commands and customize their outputs.
31
31
 
32
32
  The bot also comes with built-in AI capabilities when pinging the bot, with Ollama, Groq and Gemini as available providers.
33
33
 
34
+ ## Install
35
+
36
+ **pipx** (All platforms)
37
+ ```sh
38
+ pipx install comprobot
39
+ ```
40
+
41
+ **APT** (Ubuntu/Debian)
42
+ ```sh
43
+ sudo add-apt-repository ppa:badluma/ppa
44
+ sudo apt update && sudo apt upgrade
45
+ sudo apt install comprobot
46
+ ```
47
+
48
+ **Homebrew** (macOS)
49
+ ```sh
50
+ brew tap badluma/homebrew
51
+ brew install comprobot
52
+ ```
53
+
54
+ **Scoop** (Windows)
55
+ ```sh
56
+ scoop bucket add badluma https://github.com/badluma/scoop-bucket
57
+ scoop install comprobot
58
+ ```
59
+
60
+ Support for Docker Hub and AUR (Arch Linux) is planned.
61
+
34
62
  ## Documentation
35
63
 
36
64
  You can find the whole documentation [here](https://badluma.github.io/Comprobot-Docs/).
@@ -0,0 +1,22 @@
1
+ README.md
2
+ pyproject.toml
3
+ comprobot.egg-info/PKG-INFO
4
+ comprobot.egg-info/SOURCES.txt
5
+ comprobot.egg-info/dependency_links.txt
6
+ comprobot.egg-info/entry_points.txt
7
+ comprobot.egg-info/requires.txt
8
+ comprobot.egg-info/top_level.txt
9
+ src/__init__.py
10
+ src/__main__.py
11
+ src/api.py
12
+ src/bot.py
13
+ src/commands.py
14
+ src/data.py
15
+ src/functions.py
16
+ src/main.py
17
+ src/moderation.py
18
+ src/money_system.py
19
+ src/onboarding.py
20
+ src/process.py
21
+ src/templates.py
22
+ src/testing.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ comprobot = src.__main__:cli_main
@@ -6,7 +6,7 @@ appdirs
6
6
  ollama
7
7
  google-genai
8
8
  groq
9
- InqurererPy
9
+ InquirerPy
10
10
 
11
11
  [dev]
12
12
  ruff
@@ -0,0 +1 @@
1
+ src
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "comprobot"
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  authors = [{name = "badluma"}]
9
9
  description = "A self-hostable Discord bot built for maximum customization."
10
10
  readme = "README.md"
@@ -24,7 +24,7 @@ dependencies = [
24
24
  "ollama",
25
25
  "google-genai",
26
26
  "groq",
27
- "InqurererPy",
27
+ "InquirerPy",
28
28
  ]
29
29
 
30
30
  [project.optional-dependencies]
@@ -35,8 +35,8 @@ Homepage = "https://badluma.github.io/Comprobot-Docs"
35
35
  Repository = "https://github.com/badluma/comprobot"
36
36
 
37
37
  [project.scripts]
38
- comprobot = "Comprobot.main:main"
38
+ comprobot = "src.__main__:cli_main"
39
39
 
40
40
  [tool.setuptools.packages.find]
41
41
  where = ["."]
42
- include = ["Comprobot*"]
42
+ include = ["src*"]
@@ -6,21 +6,20 @@ from .data import active, ai, get_data_path, save_toml
6
6
  from .main import main
7
7
  from .onboarding import onboarding
8
8
 
9
- parser = argparse.ArgumentParser(
10
- prog="comprobot",
11
- description="A self-hostable open-source Discord bot built for maximum customization.",
12
- )
13
- subparsers = parser.add_subparsers(dest="command", metavar="")
14
- subparsers.add_parser("onboard", help="Set up Comprobot for the first time.")
15
- subparsers.add_parser("start", help="Start the bot.")
16
- # subparsers.add_parser("config", help="View or edit the bot's configuration.") TODO: implement config command
17
- # subparsers.add_parser("reset", help="Reset the bot's data and configuration.") TODO: implement reset command
18
9
 
19
- args = parser.parse_args()
10
+ def cli_main():
11
+ parser = argparse.ArgumentParser(
12
+ prog="comprobot",
13
+ description="A self-hostable open-source Discord bot built for maximum customization.",
14
+ )
15
+ subparsers = parser.add_subparsers(dest="command", metavar="")
16
+ subparsers.add_parser("onboard", help="Set up Comprobot for the first time.")
17
+ subparsers.add_parser("start", help="Start the bot.")
18
+ test_parser = subparsers.add_parser("test", help="Process a message through the bot's command processor.")
19
+ test_parser.add_argument("message", help="The message to process (e.g. '!calculate 2+2')")
20
20
 
21
- settings: dict = {}
21
+ args = parser.parse_args()
22
22
 
23
- if __name__ == "__main__":
24
23
  match args.command:
25
24
  case "start":
26
25
  main()
@@ -73,5 +72,13 @@ if __name__ == "__main__":
73
72
 
74
73
  main()
75
74
 
75
+ case "test":
76
+ from .testing import run_test
77
+ run_test(args.message)
78
+
76
79
  case _:
77
80
  print(parser.format_help())
81
+
82
+
83
+ if __name__ == "__main__":
84
+ cli_main()
@@ -6,10 +6,13 @@ from .data import error_messages, output
6
6
 
7
7
 
8
8
  def access_api(url, parameter, error_message, headers=None):
9
- if headers:
10
- raw = requests.get(url, headers=headers)
11
- else:
12
- raw = requests.get(url)
9
+ try:
10
+ if headers:
11
+ raw = requests.get(url, headers=headers)
12
+ else:
13
+ raw = requests.get(url)
14
+ except requests.exceptions.RequestException as e:
15
+ return (False, str(f"{error_message} ({e})"))
13
16
  if raw.status_code == 200:
14
17
  try:
15
18
  data = raw.json()
@@ -25,7 +28,10 @@ def access_api(url, parameter, error_message, headers=None):
25
28
 
26
29
  # ---------- Commands ----------
27
30
  def quote():
28
- quote_response = requests.get("https://zenquotes.io/api/random")
31
+ try:
32
+ quote_response = requests.get("https://zenquotes.io/api/random")
33
+ except requests.exceptions.RequestException as e:
34
+ return f"{error_messages['quote']} ({e})"
29
35
  if quote_response.status_code != 200:
30
36
  return f"{error_messages['quote']} (HTTP {quote_response.status_code})"
31
37
  try:
@@ -3,6 +3,7 @@ import os
3
3
  import appdirs
4
4
  import discord
5
5
  import dotenv
6
+ from discord.ext import commands
6
7
 
7
8
  dotenv.load_dotenv(
8
9
  dotenv.find_dotenv(
@@ -14,4 +15,11 @@ dotenv.load_dotenv(
14
15
 
15
16
  intents = discord.Intents.default()
16
17
  intents.message_content = True
17
- client = discord.Client(intents=intents)
18
+
19
+ from .data import config # noqa: E402
20
+
21
+ bot = commands.Bot(
22
+ command_prefix=lambda b, m: config["prefix"],
23
+ intents=intents,
24
+ )
25
+ client = bot
@@ -17,7 +17,7 @@ def ensure_file(path, content):
17
17
 
18
18
  os.makedirs(os.path.dirname(path), exist_ok=True)
19
19
  if not os.path.isfile(path):
20
- with open(path, "w") as f:
20
+ with open(path, "w", encoding="utf-8") as f:
21
21
  f.write(content)
22
22
 
23
23
 
@@ -31,18 +31,18 @@ def merge_defaults(data, defaults):
31
31
 
32
32
  def load_or_create(path, template_content):
33
33
  try:
34
- with open(path, "rb") as f:
35
- data = tomlkit.load(f)
34
+ with open(path, "r", encoding="utf-8") as f:
35
+ data = tomlkit.loads(f.read())
36
36
  except FileNotFoundError:
37
37
  ensure_file(path, template_content)
38
- with open(path, "rb") as f:
39
- data = tomlkit.load(f)
38
+ with open(path, "r", encoding="utf-8") as f:
39
+ data = tomlkit.loads(f.read())
40
40
 
41
41
  defaults = tomlkit.loads(template_content)
42
42
  merge_defaults(data, defaults)
43
43
 
44
44
  if data != defaults:
45
- with open(path, "w") as f:
45
+ with open(path, "w", encoding="utf-8") as f:
46
46
  tomlkit.dump(data, f)
47
47
 
48
48
  return data
@@ -56,7 +56,7 @@ keywords: Dict[str, Dict[str, List[str]]] = load_or_create(
56
56
  get_data_path("keywords.toml"), templates.keywords
57
57
  )
58
58
  ai: Dict[str, Any] = load_or_create(get_data_path("ai.toml"), templates.ai)
59
- system_prompt_text = ai["system_prompt"]
59
+ system_prompt_text: str = str(ai["system_prompt"])
60
60
  money: Dict[str, Dict[str, int]] = load_or_create(
61
61
  get_data_path("money.toml"), r"""members = {}"""
62
62
  )
@@ -72,5 +72,5 @@ ensure_file(get_data_path(".env"), templates.env)
72
72
 
73
73
 
74
74
  def save_toml(data, path):
75
- with open(path, "w") as f:
75
+ with open(path, "w", encoding="utf-8") as f:
76
76
  tomlkit.dump(data, f)
@@ -176,8 +176,8 @@ async def chat(message):
176
176
  {"role": "system", "content": system_prompt_text}
177
177
  ] + messages
178
178
  response = groq_client.chat.completions.create(
179
- model=ai["model"],
180
- messages=formatted_messages, # type: ignore
179
+ model=cast(str, ai["model"]),
180
+ messages=cast(Any, formatted_messages),
181
181
  )
182
182
  content = response.choices[0].message.content or ""
183
183
  else:
@@ -6,8 +6,9 @@ from os import path as os_path
6
6
  import appdirs
7
7
  import dotenv
8
8
 
9
- from . import process
10
- from .functions import client, para
9
+ from .bot import client
10
+ from .functions import para
11
+ from .process import Comprobot
11
12
 
12
13
  dotenv.load_dotenv(
13
14
  dotenv.find_dotenv(
@@ -17,8 +18,6 @@ dotenv.load_dotenv(
17
18
  )
18
19
  )
19
20
 
20
-
21
- response = None
22
21
  if sys.platform.startswith("win"):
23
22
  print("Running on Windows (cmd)")
24
23
  elif platform.system() in ["Linux", "Darwin"]:
@@ -27,9 +26,11 @@ else:
27
26
  print("Unknown OS")
28
27
 
29
28
 
30
- @client.event
31
- async def on_message(message):
32
- await process.process(message)
29
+ async def _setup_hook():
30
+ await client.add_cog(Comprobot(client))
31
+
32
+
33
+ client.setup_hook = _setup_hook
33
34
 
34
35
 
35
36
  @client.event
@@ -50,6 +50,7 @@ async def kick(message, item):
50
50
  print(f"Couldn't DM {message.author.name}")
51
51
 
52
52
 
53
+
53
54
  async def check_message(message):
54
55
  if message.guild is None:
55
56
  return
@@ -0,0 +1,411 @@
1
+ import os
2
+ from random import choice
3
+
4
+ import discord
5
+ from appdirs import user_cache_dir, user_data_dir
6
+ from discord.ext import commands as ext_commands
7
+
8
+ from . import api
9
+ from . import commands as cmd_module
10
+ from . import money_system
11
+ from .bot import bot
12
+ from .data import active, ai, config, error_messages, keywords, output
13
+ from .functions import chat
14
+ from .moderation import check_message
15
+
16
+
17
+ def _is_admin_or_bot_admin():
18
+ async def predicate(ctx):
19
+ return (
20
+ ctx.author.guild_permissions.administrator
21
+ or ctx.author.id in config["bot_admins"]
22
+ )
23
+ return ext_commands.check(predicate)
24
+
25
+
26
+ class Comprobot(ext_commands.Cog):
27
+
28
+ async def cog_before_invoke(self, ctx):
29
+ await ctx.bot.http.send_typing(ctx.channel.id)
30
+
31
+ # ── Commands ─────────────────────────────────────────────────────────────
32
+
33
+ @ext_commands.command(
34
+ name=keywords["commands"]["quote"][0],
35
+ aliases=keywords["commands"]["quote"][1:],
36
+ )
37
+ @ext_commands.check(lambda ctx: active["quote"])
38
+ async def quote_cmd(self, ctx):
39
+ await ctx.send(api.quote())
40
+
41
+ @ext_commands.command(
42
+ name=keywords["commands"]["joke"][0],
43
+ aliases=keywords["commands"]["joke"][1:],
44
+ )
45
+ @ext_commands.check(lambda ctx: active["joke"])
46
+ async def joke_cmd(self, ctx):
47
+ await ctx.send(api.joke())
48
+
49
+ @ext_commands.command(
50
+ name=keywords["commands"]["meme"][0],
51
+ aliases=keywords["commands"]["meme"][1:],
52
+ )
53
+ @ext_commands.check(lambda ctx: active["meme"])
54
+ async def meme_cmd(self, ctx):
55
+ await ctx.send(api.meme())
56
+
57
+ @ext_commands.command(
58
+ name=keywords["commands"]["waifu"][0],
59
+ aliases=keywords["commands"]["waifu"][1:],
60
+ )
61
+ @ext_commands.check(lambda ctx: active["waifu"])
62
+ async def waifu_cmd(self, ctx):
63
+ await ctx.send(api.waifu())
64
+
65
+ @ext_commands.group(
66
+ name=keywords["commands"]["image"][0],
67
+ aliases=keywords["commands"]["image"][1:],
68
+ invoke_without_command=True,
69
+ )
70
+ @ext_commands.check(lambda ctx: active["image"])
71
+ async def image_cmd(self, ctx):
72
+ await ctx.send(error_messages["missing_argument"])
73
+
74
+ @image_cmd.command(
75
+ name=keywords["commands"]["duck"][0],
76
+ aliases=keywords["commands"]["duck"][1:],
77
+ )
78
+ @ext_commands.check(lambda ctx: active["duck"])
79
+ async def duck_cmd(self, ctx):
80
+ await ctx.send(api.duck())
81
+
82
+ @image_cmd.command(
83
+ name=keywords["commands"]["dog"][0],
84
+ aliases=keywords["commands"]["dog"][1:],
85
+ )
86
+ @ext_commands.check(lambda ctx: active["dog"])
87
+ async def dog_cmd(self, ctx):
88
+ await ctx.send(api.dog())
89
+
90
+ @image_cmd.command(
91
+ name=keywords["commands"]["cat"][0],
92
+ aliases=keywords["commands"]["cat"][1:],
93
+ )
94
+ @ext_commands.check(lambda ctx: active["cat"])
95
+ async def cat_cmd(self, ctx):
96
+ await ctx.send(api.cat())
97
+
98
+ @ext_commands.command(
99
+ name=keywords["commands"]["chuck_norris"][0],
100
+ aliases=keywords["commands"]["chuck_norris"][1:],
101
+ )
102
+ @ext_commands.check(lambda ctx: active["chuck_norris"])
103
+ async def chuck_cmd(self, ctx):
104
+ await ctx.send(api.chuck())
105
+
106
+ @ext_commands.command(
107
+ name=keywords["commands"]["fact"][0],
108
+ aliases=keywords["commands"]["fact"][1:],
109
+ )
110
+ @ext_commands.check(lambda ctx: active["fact"])
111
+ async def fact_cmd(self, ctx):
112
+ await ctx.send(api.fact())
113
+
114
+ @ext_commands.command(
115
+ name=keywords["commands"]["bible"][0],
116
+ aliases=keywords["commands"]["bible"][1:],
117
+ )
118
+ @ext_commands.check(lambda ctx: active["bible"])
119
+ async def bible_cmd(self, ctx, book: str | None = None, chapter: int | None = None, verse: int | None = None):
120
+ if book is None:
121
+ await ctx.send(api.bible(True))
122
+ elif chapter is not None and verse is not None:
123
+ await ctx.send(api.bible(False, book, chapter, verse))
124
+ else:
125
+ await ctx.send(error_messages["missing_argument"])
126
+
127
+ @ext_commands.command(
128
+ name=keywords["commands"]["truth"][0],
129
+ aliases=keywords["commands"]["truth"][1:],
130
+ )
131
+ @ext_commands.check(lambda ctx: active["truth"])
132
+ async def truth_cmd(self, ctx, rating: str | None = None):
133
+ result = api.tord("https://api.truthordarebot.xyz/v1/truth", rating)
134
+ if result:
135
+ await ctx.send(choice(output["commands"]["truth"]).replace("{{QUESTION}}", result))
136
+ else:
137
+ await ctx.send(error_messages["truth"])
138
+
139
+ @ext_commands.command(
140
+ name=keywords["commands"]["dare"][0],
141
+ aliases=keywords["commands"]["dare"][1:],
142
+ )
143
+ @ext_commands.check(lambda ctx: active["dare"])
144
+ async def dare_cmd(self, ctx, rating: str | None = None):
145
+ result = api.tord("https://api.truthordarebot.xyz/api/dare", rating)
146
+ if result:
147
+ await ctx.send(choice(output["commands"]["dare"]).replace("{{QUESTION}}", result))
148
+ else:
149
+ await ctx.send(error_messages["dare"])
150
+
151
+ @ext_commands.command(
152
+ name=keywords["commands"]["wyr"][0],
153
+ aliases=keywords["commands"]["wyr"][1:],
154
+ )
155
+ @ext_commands.check(lambda ctx: active["wyr"])
156
+ async def wyr_cmd(self, ctx, rating: str | None = None):
157
+ result = api.tord("https://api.truthordarebot.xyz/api/wyr", rating)
158
+ if result:
159
+ await ctx.send(choice(output["commands"]["wyr"]).replace("{{QUESTION}}", result))
160
+ else:
161
+ await ctx.send(error_messages["wyr"])
162
+
163
+ @ext_commands.command(
164
+ name=keywords["commands"]["never_have_i_ever"][0],
165
+ aliases=keywords["commands"]["never_have_i_ever"][1:],
166
+ )
167
+ @ext_commands.check(lambda ctx: active["never_have_i_ever"])
168
+ async def nhie_cmd(self, ctx, rating: str | None = None):
169
+ result = api.tord("https://api.truthordarebot.xyz/api/nhie", rating)
170
+ if result:
171
+ await ctx.send(choice(output["commands"]["never_have_i_ever"]).replace("{{QUESTION}}", result))
172
+ else:
173
+ await ctx.send(error_messages["never-hie"])
174
+
175
+ @ext_commands.command(
176
+ name=keywords["commands"]["paranoia"][0],
177
+ aliases=keywords["commands"]["paranoia"][1:],
178
+ )
179
+ @ext_commands.check(lambda ctx: active["paranoia"])
180
+ async def paranoia_cmd(self, ctx, rating: str | None = None):
181
+ result = api.tord("https://api.truthordarebot.xyz/api/paranoia", rating)
182
+ if result:
183
+ await ctx.send(choice(output["commands"]["paranoia"]).replace("{{QUESTION}}", result))
184
+ else:
185
+ await ctx.send(error_messages["paranoia"])
186
+
187
+ @ext_commands.command(
188
+ name=keywords["commands"]["qr_code"][0],
189
+ aliases=keywords["commands"]["qr_code"][1:],
190
+ )
191
+ @ext_commands.check(lambda ctx: active["qr_code"])
192
+ async def qr_cmd(self, ctx, link: str | None = None):
193
+ if link:
194
+ await ctx.send(cmd_module.qr(link))
195
+ else:
196
+ await ctx.send(error_messages["missing_argument"])
197
+
198
+ @ext_commands.command(
199
+ name=keywords["commands"]["calculate"][0],
200
+ aliases=keywords["commands"]["calculate"][1:],
201
+ )
202
+ @ext_commands.check(lambda ctx: active["calculate"])
203
+ async def calculate_cmd(self, ctx, *, expression: str | None = None):
204
+ if expression:
205
+ await ctx.send(cmd_module.calculate(expression))
206
+ else:
207
+ await ctx.send(error_messages["missing_argument"])
208
+
209
+ @ext_commands.command(
210
+ name=keywords["commands"]["bitcoin"][0],
211
+ aliases=keywords["commands"]["bitcoin"][1:],
212
+ )
213
+ @ext_commands.check(lambda ctx: active["bitcoin"])
214
+ async def bitcoin_cmd(self, ctx, currency: str = "usd"):
215
+ await ctx.send(api.bitcoin(currency))
216
+
217
+ @ext_commands.command(
218
+ name=keywords["commands"]["ascii_art"][0],
219
+ aliases=keywords["commands"]["ascii_art"][1:],
220
+ )
221
+ async def ascii_cmd(self, ctx):
222
+ await ctx.send(cmd_module.ascii())
223
+
224
+ @ext_commands.command(
225
+ name=keywords["commands"]["currency"][0],
226
+ aliases=keywords["commands"]["currency"][1:],
227
+ )
228
+ @ext_commands.check(lambda ctx: active["currency"])
229
+ async def currency_cmd(self, ctx, amount: str | None = None, from_currency: str | None = None, to_currency: str | None = None):
230
+ if not all([amount, from_currency, to_currency]):
231
+ await ctx.send(error_messages["missing_argument"])
232
+ return
233
+ await ctx.send(api.currency(from_currency, to_currency, amount))
234
+
235
+ # ── Money ─────────────────────────────────────────────────────────────────
236
+
237
+ @ext_commands.command(
238
+ name=keywords["money"]["check_balance"][0],
239
+ aliases=keywords["money"]["check_balance"][1:],
240
+ )
241
+ async def check_balance_cmd(self, ctx, username: str | None = None):
242
+ if username:
243
+ await ctx.send(money_system.check_balance(username))
244
+ else:
245
+ await ctx.send(error_messages["missing_argument"])
246
+
247
+ @ext_commands.command(
248
+ name=keywords["money"]["add_money"][0],
249
+ aliases=keywords["money"]["add_money"][1:],
250
+ )
251
+ @_is_admin_or_bot_admin()
252
+ async def add_money_cmd(self, ctx, username: str | None = None, amount: str | None = None):
253
+ if not username or not amount:
254
+ await ctx.send(error_messages["missing_argument"])
255
+ return
256
+ try:
257
+ await ctx.send(money_system.add_money(username, int(amount)))
258
+ except ValueError:
259
+ await ctx.send(f"Invalid amount: {amount}")
260
+
261
+ @ext_commands.command(
262
+ name=keywords["money"]["remove_money"][0],
263
+ aliases=keywords["money"]["remove_money"][1:],
264
+ )
265
+ @_is_admin_or_bot_admin()
266
+ async def remove_money_cmd(self, ctx, username: str | None = None, amount: str | None = None):
267
+ if not username or not amount:
268
+ await ctx.send(error_messages["missing_argument"])
269
+ return
270
+ try:
271
+ await ctx.send(money_system.remove_money(username, int(amount)))
272
+ except ValueError:
273
+ await ctx.send(f"Invalid amount: {amount}")
274
+
275
+ @ext_commands.command(name="purge")
276
+ @ext_commands.check(lambda ctx: active["purge"])
277
+ @ext_commands.has_permissions(administrator=True)
278
+ async def purge_cmd(self, ctx):
279
+ await ctx.channel.purge()
280
+
281
+ # ── Settings ──────────────────────────────────────────────────────────────
282
+
283
+ @ext_commands.group(
284
+ name=keywords["settings"]["settings"][0],
285
+ aliases=keywords["settings"]["settings"][1:],
286
+ invoke_without_command=True,
287
+ )
288
+ async def settings_cmd(self, ctx):
289
+ await ctx.send(error_messages["unknown_command"])
290
+
291
+ @settings_cmd.command(
292
+ name=keywords["settings"]["profile_picture"][0],
293
+ aliases=keywords["settings"]["profile_picture"][1:],
294
+ )
295
+ @_is_admin_or_bot_admin()
296
+ async def pfp_cmd(self, ctx):
297
+ if not ctx.message.attachments:
298
+ await ctx.send(error_messages["no_attachment"])
299
+ return
300
+ cache_dir = user_cache_dir("Comprobot", appauthor=False)
301
+ os.makedirs(cache_dir, exist_ok=True)
302
+ await ctx.message.attachments[0].save(f"{cache_dir}/pfp.png")
303
+ if bot.user is None:
304
+ await ctx.send(error_messages["bot_unavailable"])
305
+ return
306
+ with open(f"{cache_dir}/pfp.png", "rb") as f:
307
+ await bot.user.edit(avatar=f.read())
308
+ await ctx.send(choice(output["settings"]["profile_picture_applied"]))
309
+
310
+ @settings_cmd.command(
311
+ name=keywords["settings"]["banner"][0],
312
+ aliases=keywords["settings"]["banner"][1:],
313
+ )
314
+ @_is_admin_or_bot_admin()
315
+ async def banner_cmd(self, ctx):
316
+ if not ctx.message.attachments:
317
+ await ctx.send(error_messages["no_attachment"])
318
+ return
319
+ cache_dir = user_cache_dir("Comprobot", appauthor=False)
320
+ os.makedirs(cache_dir, exist_ok=True)
321
+ await ctx.message.attachments[0].save(f"{cache_dir}/banner.png")
322
+ if bot.user is None:
323
+ await ctx.send(error_messages["bot_unavailable"])
324
+ return
325
+ with open(f"{cache_dir}/banner.png", "rb") as f:
326
+ await bot.user.edit(banner=f.read())
327
+ await ctx.send(choice(output["settings"]["banner_applied"]))
328
+
329
+ @settings_cmd.command(
330
+ name=keywords["settings"]["change_name"][0],
331
+ aliases=keywords["settings"]["change_name"][1:],
332
+ )
333
+ @_is_admin_or_bot_admin()
334
+ async def name_cmd(self, ctx, *, name: str | None = None):
335
+ if not name:
336
+ await ctx.send(error_messages["missing_argument"])
337
+ return
338
+ if bot.user is None:
339
+ await ctx.send(error_messages["bot_unavailable"])
340
+ return
341
+ await bot.user.edit(username=name)
342
+ await ctx.send(choice(output["settings"]["nickname_applied"]).replace("{{NAME}}", name))
343
+
344
+ @settings_cmd.command(
345
+ name=keywords["settings"]["change_keywords"][0],
346
+ aliases=keywords["settings"]["change_keywords"][1:],
347
+ )
348
+ @_is_admin_or_bot_admin()
349
+ async def keywords_cmd(self, ctx, command_name: str | None = None, *new_keywords):
350
+ if not command_name or not new_keywords:
351
+ await ctx.send(error_messages["missing_argument"])
352
+ return
353
+ from . import data as data_module
354
+
355
+ target_category = None
356
+ for category in data_module.keywords:
357
+ if command_name in data_module.keywords[category]:
358
+ target_category = category
359
+ break
360
+ if target_category is None:
361
+ await ctx.send(error_messages["unknown_argument"])
362
+ return
363
+ data_module.keywords[target_category][command_name] = list(new_keywords)
364
+ data_module.save_toml(
365
+ data_module.keywords,
366
+ f"{user_data_dir('Comprobot')}/keywords.toml",
367
+ )
368
+ await ctx.send(
369
+ choice(output["settings"]["keywords_applied"])
370
+ .replace("{{KEYWORDS}}", " ".join(new_keywords))
371
+ .replace("{{COMMAND}}", command_name)
372
+ )
373
+
374
+ # ── Events ────────────────────────────────────────────────────────────────
375
+
376
+ @ext_commands.Cog.listener()
377
+ async def on_message(self, message):
378
+ print(
379
+ "\033[90m"
380
+ + f"[{message.channel}] "
381
+ + "\033[36m"
382
+ + f"{message.author.name}: "
383
+ + "\033[0m"
384
+ + message.content
385
+ )
386
+
387
+ if message.author == bot.user:
388
+ return
389
+
390
+ await check_message(message)
391
+
392
+ user_id = bot.user.id if bot.user else None
393
+
394
+ is_reply_to_bot = False
395
+ if message.reference and message.reference.message_id and user_id:
396
+ try:
397
+ ref_msg = await message.channel.fetch_message(message.reference.message_id)
398
+ is_reply_to_bot = ref_msg.author.id == user_id
399
+ except discord.NotFound:
400
+ pass
401
+
402
+ if (
403
+ f"<@{user_id}>" in message.content
404
+ or (is_reply_to_bot and ai["answer_to_reply"])
405
+ ) and ai["activate_ai"]:
406
+ async with message.channel.typing():
407
+ response = await chat(message)
408
+ if response:
409
+ content = str(response)
410
+ for chunk in [content[i : i + 2000] for i in range(0, len(content), 2000)]:
411
+ await message.channel.send(chunk)
@@ -37,9 +37,7 @@ user_prompt_structure = """
37
37
  '''
38
38
 
39
39
  config = r"""
40
- command_prefix = "!"
41
- settings_prefix = "s!"
42
- music_prefix = "m!"
40
+ prefix = "!"
43
41
 
44
42
  money_symbol = "$"
45
43
  bot_admins = []
@@ -0,0 +1,123 @@
1
+ import asyncio
2
+ import inspect
3
+ import sys
4
+ from typing import Any, cast
5
+
6
+ from discord.ext import commands as ext_commands
7
+
8
+ from .data import config
9
+ from .process import Comprobot
10
+
11
+
12
+ class _FakeHTTP:
13
+ async def send_typing(self, _):
14
+ pass
15
+
16
+
17
+ class _FakePermissions:
18
+ administrator = True
19
+
20
+
21
+ class _FakeAuthor:
22
+ id = 0
23
+ name = "tester"
24
+ guild_permissions = _FakePermissions()
25
+
26
+
27
+ class _FakeChannel:
28
+ id = 0
29
+
30
+ async def purge(self):
31
+ pass
32
+
33
+
34
+ class _FakeBotUser:
35
+ id = 1
36
+
37
+
38
+ class _FakeBot:
39
+ http = _FakeHTTP()
40
+ user = _FakeBotUser()
41
+
42
+
43
+ class _FakeMessage:
44
+ attachments = []
45
+ reference = None
46
+
47
+
48
+ class _FakeCtx:
49
+ def __init__(self):
50
+ self.author = _FakeAuthor()
51
+ self.channel = _FakeChannel()
52
+ self.message = _FakeMessage()
53
+ self.bot = _FakeBot()
54
+
55
+ async def send(self, content):
56
+ print(content)
57
+
58
+
59
+ def _find_command(cog, parts):
60
+ """Return (command, remaining_args) or (None, [])."""
61
+ if not parts:
62
+ return None, []
63
+
64
+ cmd_name = parts[0].lower()
65
+ remaining = parts[1:]
66
+
67
+ for command in cog.__cog_commands__:
68
+ if command.parent is not None:
69
+ continue
70
+ names = [command.name] + list(command.aliases or [])
71
+ if cmd_name not in names:
72
+ continue
73
+ if isinstance(command, ext_commands.Group) and remaining:
74
+ sub = command.all_commands.get(remaining[0].lower())
75
+ if sub:
76
+ return sub, remaining[1:]
77
+ return command, remaining
78
+
79
+ return None, []
80
+
81
+
82
+ async def _run(message: str) -> int:
83
+ prefix = config.get("prefix", "!")
84
+ if not message.startswith(prefix):
85
+ print(f"Error: message must start with prefix '{prefix}'")
86
+ return 1
87
+
88
+ parts = message[len(prefix):].split()
89
+ if not parts:
90
+ print("Error: empty command")
91
+ return 1
92
+
93
+ cog = Comprobot()
94
+ ctx: Any = _FakeCtx()
95
+ await cog.cog_before_invoke(ctx)
96
+
97
+ command, args = _find_command(cog, parts)
98
+ if command is None:
99
+ print(f"Error: unknown command '{parts[0]}'")
100
+ return 1
101
+
102
+ sig = inspect.signature(command.callback)
103
+ params = list(sig.parameters.values())[2:] # skip self, ctx
104
+ kw_only = [p for p in params if p.kind == inspect.Parameter.KEYWORD_ONLY]
105
+
106
+ fn = cast(Any, command.callback)
107
+ try:
108
+ if kw_only and args:
109
+ await fn(cog, ctx, **{kw_only[0].name: " ".join(args)})
110
+ else:
111
+ await fn(cog, ctx, *args)
112
+ except Exception as e:
113
+ print(f"Error: {e}")
114
+ return 1
115
+
116
+ return 0
117
+
118
+
119
+ def run_test(message: str) -> None:
120
+ import io
121
+ if isinstance(sys.stdout, io.TextIOWrapper):
122
+ sys.stdout.reconfigure(encoding="utf-8")
123
+ sys.exit(asyncio.run(_run(message)))
@@ -1,256 +0,0 @@
1
- import os
2
- from random import choice
3
- from typing import Any, cast
4
-
5
- import discord
6
- from appdirs import user_cache_dir, user_data_dir
7
-
8
- from . import api, commands, data, money_system
9
- from .bot import client
10
- from .data import active, ai, config, error_messages, keywords, output
11
- from .functions import chat
12
- from .moderation import check_message
13
-
14
-
15
- async def command(message) -> str | None | Any:
16
-
17
- command_parts = message.content[len(config["command_prefix"]) :].strip().split()
18
-
19
- command = command_parts[0]
20
- args = command_parts[1:]
21
-
22
- if command in keywords["commands"]["quote"] and active["quote"]:
23
- return api.quote()
24
- elif command in keywords["commands"]["joke"] and active["joke"]:
25
- return api.joke()
26
- elif command in keywords["commands"]["meme"] and active["meme"]:
27
- return api.meme()
28
- elif command in keywords["commands"]["waifu"] and active["waifu"]:
29
- return api.waifu()
30
- elif command in keywords["commands"]["image"] and active["image"]:
31
- if args[0] in keywords["commands"]["duck"] and active["duck"]:
32
- return api.duck()
33
- elif args[0] in keywords["commands"]["dog"] and active["dog"]:
34
- return api.dog()
35
- elif args[0] in keywords["commands"]["cat"] and active["cat"]:
36
- return api.cat()
37
- elif not args:
38
- return error_messages["missing_argument"]
39
- else:
40
- return error_messages["unknown_argument"]
41
- elif command in keywords["commands"]["chuck_norris"] and active["chuck_norris"]:
42
- return api.chuck()
43
- elif command in keywords["commands"]["fact"] and active["fact"]:
44
- return api.fact()
45
- elif command in keywords["commands"]["bible"] and active["bible"]:
46
- if not args:
47
- return api.bible(True)
48
- if len(args) >= 3:
49
- return api.bible(False, args[0], args[1], args[2])
50
- return error_messages["passage_not_found"]
51
- elif command in keywords["commands"]["truth"] and active["truth"]:
52
- return api.tord(
53
- "https://api.truthordarebot.xyz/v1/truth", args[0] if args else None
54
- )
55
- elif command in keywords["commands"]["dare"] and active["dare"]:
56
- return api.tord(
57
- "https://api.truthordarebot.xyz/api/dare", args[0] if args else None
58
- )
59
- elif command in keywords["commands"]["wyr"] and active["wyr"]:
60
- return api.tord(
61
- "https://api.truthordarebot.xyz/api/wyr", args[0] if args else None
62
- )
63
- elif (
64
- command in keywords["commands"]["never_have_i_ever"]
65
- and active["never_have_i_ever"]
66
- ):
67
- return api.tord(
68
- "https://api.truthordarebot.xyz/api/nhie", args[0] if args else None
69
- )
70
- elif command in keywords["commands"]["paranoia"] and active["paranoia"]:
71
- return api.tord(
72
- "https://api.truthordarebot.xyz/api/paranoia", args[0] if args else None
73
- )
74
-
75
- elif command in keywords["commands"]["qr_code"] and active["qr_code"]:
76
- if args:
77
- return commands.qr(args[0])
78
- else:
79
- return error_messages["missing_argument"]
80
-
81
- elif command in keywords["commands"]["calculate"]:
82
- if args:
83
- return commands.calculate(args[0])
84
- else:
85
- return error_messages["missing_argument"]
86
-
87
- elif command in keywords["commands"]["bitcoin"] and active["bitcoin"]:
88
- return api.bitcoin(args[0] if args else "usd")
89
-
90
- elif command in keywords["commands"]["ascii_art"]:
91
- return commands.ascii()
92
-
93
- elif command in keywords["commands"]["currency"] and active["currency"]:
94
- if not len(args) >= 3:
95
- return error_messages["missing_argument"]
96
- return api.currency(args[1], args[2], args[0])
97
-
98
- elif command in keywords["money"]["check_balance"]:
99
- if args:
100
- return money_system.check_balance(args[0])
101
- else:
102
- return error_messages["missing_argument"]
103
- elif command in keywords["money"]["add_money"] and (
104
- message.author.guild_permissions.administrator or config["bot_admins"]
105
- ):
106
- if len(args) >= 2:
107
- try:
108
- amount = int(args[1])
109
- return money_system.add_money(args[0], amount)
110
- except ValueError:
111
- return f"Invalid amount: {args[1]}"
112
- else:
113
- return "No amount given."
114
- elif command in keywords["money"]["remove_money"] and (
115
- message.author.guild_permissions.administrator or config["bot_admins"]
116
- ):
117
- if len(args) >= 2:
118
- try:
119
- amount = int(args[1])
120
- return money_system.remove_money(args[0], amount)
121
- except ValueError:
122
- return f"Invalid amount: {args[1]}"
123
- else:
124
- return "No amount given."
125
-
126
- elif command == "purge" and active["purge"]:
127
- if message.author.guild_permissions.administrator:
128
- await message.channel.purge()
129
- return "All messages deleted."
130
-
131
- else:
132
- return None
133
-
134
-
135
- async def settings(message):
136
-
137
- command_parts = message.content[len(config["settings_prefix"]) :].strip().split()
138
-
139
- command = command_parts[0]
140
- args = command_parts[1:]
141
-
142
- cache_dir = user_cache_dir("Comprobot", appauthor=False)
143
- os.makedirs(cache_dir, exist_ok=True)
144
-
145
- if command in keywords["settings"]["profile_picture"]:
146
- if not message.attachments:
147
- return error_messages["no_attachments"]
148
- if client is None or client.user is None:
149
- return error_messages["bot_unavailable"]
150
- new_pfp = message.attachments[0]
151
- await new_pfp.save(f"{cache_dir}/pfp.png")
152
- with open(f"{cache_dir}/pfp.png", "rb") as image_file:
153
- image_data = image_file.read()
154
- await client.user.edit(avatar=image_data)
155
- return choice(output["settings"]["profile_picture_applied"])
156
-
157
- elif command in keywords["settings"]["banner"]:
158
- if not message.attachments:
159
- return error_messages["no_attachments"]
160
- if client is None or client.user is None:
161
- return error_messages["bot_unavailable"]
162
- new_banner = message.attachments[0]
163
- await new_banner.save(f"{cache_dir}/banner.png")
164
- with open(f"{cache_dir}/banner.png", "rb") as image_file:
165
- image_data = image_file.read()
166
- await client.user.edit(banner=image_data)
167
- return choice(output["settings"]["banner_applied"])
168
-
169
- elif command in keywords["settings"]["change_name"]:
170
- if len(args) < 2:
171
- return error_messages["missing_argument"]
172
- if client is None or client.user is None:
173
- return error_messages["bot_unavailable"]
174
- await client.user.edit(username=args[1])
175
- return choice(output["settings"]["nickname_applied"]).replace(
176
- "{{NAME}}", " ".join(args[1:])
177
- )
178
-
179
- elif command in keywords["settings"]["change_keywords"]:
180
- if len(args) < 2:
181
- return error_messages["missing_argument"]
182
-
183
- target_category = None
184
- for category in keywords:
185
- if args[0] in keywords[category]:
186
- target_category = category
187
- break
188
-
189
- if target_category is None:
190
- return error_messages["unknown_argument"]
191
-
192
- keywords[target_category][args[0]] = args[1:]
193
-
194
- data.save_toml(keywords, f"{user_data_dir('Comprobot')}/keywords.toml")
195
-
196
- return (
197
- choice(output["settings"]["keywords_applied"])
198
- .replace("{{KEYWORDS}}", " ".join(args[1:]))
199
- .replace("{{COMMAND}}", args[0])
200
- )
201
-
202
- else:
203
- return error_messages["unknown_command"]
204
-
205
-
206
- async def games(message):
207
- pass
208
-
209
-
210
- async def process(message):
211
- print(
212
- "\033[90m"
213
- + f"[{message.channel}] "
214
- + "\033[36m"
215
- + f"{message.author.name}: "
216
- + "\033[0m"
217
- + message.content
218
- )
219
-
220
- global response
221
-
222
- if message.author == client.user:
223
- return
224
- response = None
225
-
226
- await check_message(message)
227
-
228
- if message.content.startswith(cast(str, config["command_prefix"])):
229
- async with message.channel.typing():
230
- response = await command(message)
231
- elif message.content.startswith(cast(str, config["settings_prefix"])):
232
- async with message.channel.typing():
233
- response = await settings(message)
234
-
235
- is_reply_to_bot = False
236
- user_id = client.user.id if client.user else None
237
- if message.reference and message.reference.message_id and user_id:
238
- try:
239
- ref_msg = await message.channel.fetch_message(message.reference.message_id)
240
- is_reply_to_bot = ref_msg.author.id == user_id
241
- except discord.NotFound:
242
- pass
243
-
244
- if (
245
- f"<@{user_id}>" in message.content
246
- or (is_reply_to_bot if ai["answer_to_reply"] else False)
247
- and ai["activate_ai"]
248
- ):
249
- async with message.channel.typing():
250
- response = await chat(message)
251
-
252
- if response:
253
- async with message.channel.typing():
254
- content = str(response)
255
- for chunk in [content[i : i + 2000] for i in range(0, len(content), 2000)]:
256
- await message.channel.send(chunk)
comprobot-2.1.0/README.md DELETED
@@ -1,15 +0,0 @@
1
- # Comprobot
2
-
3
- Comprobot is a highly-customizable, open-source Discord bot that you can run on your own server.
4
-
5
- It’s built with Python, has a wide range of fun and useful commands, and is designed to be easy to extend. You can add new commands, customize outputs , or change the behaviour of existing ones. You can also easily edit the keywords of existing commands and customize their outputs.
6
-
7
- The bot also comes with built-in AI capabilities when pinging the bot, with Ollama, Groq and Gemini as available providers.
8
-
9
- ## Documentation
10
-
11
- You can find the whole documentation [here](https://badluma.github.io/Comprobot-Docs/).
12
-
13
- ## License
14
-
15
- MIT
@@ -1,21 +0,0 @@
1
- README.md
2
- pyproject.toml
3
- Comprobot/__init__.py
4
- Comprobot/__main__.py
5
- Comprobot/api.py
6
- Comprobot/bot.py
7
- Comprobot/commands.py
8
- Comprobot/data.py
9
- Comprobot/functions.py
10
- Comprobot/main.py
11
- Comprobot/moderation.py
12
- Comprobot/money_system.py
13
- Comprobot/onboarding.py
14
- Comprobot/process.py
15
- Comprobot/templates.py
16
- comprobot.egg-info/PKG-INFO
17
- comprobot.egg-info/SOURCES.txt
18
- comprobot.egg-info/dependency_links.txt
19
- comprobot.egg-info/entry_points.txt
20
- comprobot.egg-info/requires.txt
21
- comprobot.egg-info/top_level.txt
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- comprobot = Comprobot.main:main
@@ -1 +0,0 @@
1
- Comprobot
File without changes