comprobot 2.0.4__tar.gz → 2.1.0__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 (25) hide show
  1. comprobot-2.1.0/Comprobot/__main__.py +77 -0
  2. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/bot.py +3 -2
  3. comprobot-2.1.0/Comprobot/data.py +76 -0
  4. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/functions.py +3 -2
  5. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/money_system.py +4 -4
  6. comprobot-2.1.0/Comprobot/onboarding.py +180 -0
  7. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/templates.py +1 -10
  8. {comprobot-2.0.4/comprobot.egg-info → comprobot-2.1.0}/PKG-INFO +3 -2
  9. {comprobot-2.0.4 → comprobot-2.1.0}/README.md +1 -1
  10. {comprobot-2.0.4 → comprobot-2.1.0/comprobot.egg-info}/PKG-INFO +3 -2
  11. {comprobot-2.0.4 → comprobot-2.1.0}/comprobot.egg-info/SOURCES.txt +1 -0
  12. {comprobot-2.0.4 → comprobot-2.1.0}/comprobot.egg-info/requires.txt +1 -0
  13. {comprobot-2.0.4 → comprobot-2.1.0}/pyproject.toml +2 -1
  14. comprobot-2.0.4/Comprobot/__main__.py +0 -4
  15. comprobot-2.0.4/Comprobot/data.py +0 -84
  16. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/__init__.py +0 -0
  17. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/api.py +0 -0
  18. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/commands.py +0 -0
  19. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/main.py +0 -0
  20. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/moderation.py +0 -0
  21. {comprobot-2.0.4 → comprobot-2.1.0}/Comprobot/process.py +0 -0
  22. {comprobot-2.0.4 → comprobot-2.1.0}/comprobot.egg-info/dependency_links.txt +0 -0
  23. {comprobot-2.0.4 → comprobot-2.1.0}/comprobot.egg-info/entry_points.txt +0 -0
  24. {comprobot-2.0.4 → comprobot-2.1.0}/comprobot.egg-info/top_level.txt +0 -0
  25. {comprobot-2.0.4 → comprobot-2.1.0}/setup.cfg +0 -0
@@ -0,0 +1,77 @@
1
+ import argparse
2
+
3
+ import dotenv
4
+
5
+ from .data import active, ai, get_data_path, save_toml
6
+ from .main import main
7
+ from .onboarding import onboarding
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
+
19
+ args = parser.parse_args()
20
+
21
+ settings: dict = {}
22
+
23
+ if __name__ == "__main__":
24
+ match args.command:
25
+ case "start":
26
+ main()
27
+ case "onboard":
28
+ settings = onboarding()
29
+
30
+ dotenv.set_key(
31
+ dotenv_path=".env",
32
+ key_to_set="BOT_TOKEN",
33
+ value_to_set=str(settings["token"]),
34
+ )
35
+
36
+ for key in active:
37
+ if key in settings["commands_activated"]:
38
+ active[key] = True
39
+ else:
40
+ active[key] = False
41
+
42
+ ai["activated"] = settings["ai_activated"]
43
+
44
+ ai["provider"] = settings["provider"]
45
+
46
+ if settings["provider"] == "groq":
47
+ dotenv.set_key(
48
+ dotenv_path=".env",
49
+ key_to_set="GROQ",
50
+ value_to_set=settings["api_key"],
51
+ )
52
+ dotenv.set_key(dotenv_path=".env", key_to_set="GEMINI", value_to_set="")
53
+
54
+ elif settings["provider"] == "gemini":
55
+ dotenv.set_key(
56
+ dotenv_path=".env",
57
+ key_to_set="GEMINI",
58
+ value_to_set=settings["api_key"],
59
+ )
60
+ dotenv.set_key(dotenv_path=".env", key_to_set="GROQ", value_to_set="")
61
+
62
+ else:
63
+ dotenv.set_key(dotenv_path=".env", key_to_set="GROQ", value_to_set="")
64
+ dotenv.set_key(dotenv_path=".env", key_to_set="GEMINI", value_to_set="")
65
+
66
+ if settings["model"]:
67
+ ai["model"] = settings["model"]
68
+ else:
69
+ ai["model"] = ""
70
+
71
+ save_toml(ai, get_data_path("ai.toml"))
72
+ save_toml(active, get_data_path("active.toml"))
73
+
74
+ main()
75
+
76
+ case _:
77
+ print(parser.format_help())
@@ -1,7 +1,8 @@
1
- import discord
2
1
  import os
3
- import dotenv
2
+
4
3
  import appdirs
4
+ import discord
5
+ import dotenv
5
6
 
6
7
  dotenv.load_dotenv(
7
8
  dotenv.find_dotenv(
@@ -0,0 +1,76 @@
1
+ import os
2
+ from typing import Any, Dict, List
3
+
4
+ import appdirs
5
+ import tomlkit
6
+
7
+ from . import templates
8
+
9
+
10
+ def get_data_path(filename):
11
+
12
+ base_dir = appdirs.user_data_dir(appname="Comprobot", appauthor=False)
13
+ return os.path.join(base_dir, filename)
14
+
15
+
16
+ def ensure_file(path, content):
17
+
18
+ os.makedirs(os.path.dirname(path), exist_ok=True)
19
+ if not os.path.isfile(path):
20
+ with open(path, "w") as f:
21
+ f.write(content)
22
+
23
+
24
+ def merge_defaults(data, defaults):
25
+ for key, value in defaults.items():
26
+ if key not in data:
27
+ data[key] = value
28
+ elif isinstance(value, dict) and isinstance(data.get(key), dict):
29
+ merge_defaults(data[key], value)
30
+
31
+
32
+ def load_or_create(path, template_content):
33
+ try:
34
+ with open(path, "rb") as f:
35
+ data = tomlkit.load(f)
36
+ except FileNotFoundError:
37
+ ensure_file(path, template_content)
38
+ with open(path, "rb") as f:
39
+ data = tomlkit.load(f)
40
+
41
+ defaults = tomlkit.loads(template_content)
42
+ merge_defaults(data, defaults)
43
+
44
+ if data != defaults:
45
+ with open(path, "w") as f:
46
+ tomlkit.dump(data, f)
47
+
48
+ return data
49
+
50
+
51
+ error_messages: Dict[str, str] = load_or_create(
52
+ get_data_path("error-messages.toml"), templates.error_messages
53
+ )
54
+ config: Dict[str, Any] = load_or_create(get_data_path("config.toml"), templates.config)
55
+ keywords: Dict[str, Dict[str, List[str]]] = load_or_create(
56
+ get_data_path("keywords.toml"), templates.keywords
57
+ )
58
+ ai: Dict[str, Any] = load_or_create(get_data_path("ai.toml"), templates.ai)
59
+ system_prompt_text = ai["system_prompt"]
60
+ money: Dict[str, Dict[str, int]] = load_or_create(
61
+ get_data_path("money.toml"), r"""members = {}"""
62
+ )
63
+ active: Dict[str, bool] = load_or_create(get_data_path("active.toml"), templates.active)
64
+ output: Dict[str, Dict[str, List[str]]] = load_or_create(
65
+ get_data_path("output.toml"), templates.output
66
+ )
67
+ moderation: Dict[Any, Any] = load_or_create(
68
+ get_data_path("moderation.toml"), templates.moderation
69
+ )
70
+
71
+ ensure_file(get_data_path(".env"), templates.env)
72
+
73
+
74
+ def save_toml(data, path):
75
+ with open(path, "w") as f:
76
+ tomlkit.dump(data, f)
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import re
3
+ import subprocess
3
4
  from typing import Any, Dict, List, cast
4
5
 
5
6
  import appdirs
@@ -22,9 +23,9 @@ def para(count=1):
22
23
 
23
24
  def clear():
24
25
  if os.name == "nt":
25
- os.system("cls")
26
+ subprocess.call("cls")
26
27
  else:
27
- os.system("clear")
28
+ subprocess.call("clear")
28
29
 
29
30
 
30
31
  async def direct_msg(message, author_message):
@@ -6,7 +6,7 @@ import tomlkit.exceptions
6
6
 
7
7
  def add_money(username, amount):
8
8
  data.money["members"][username] = data.money["members"].get(username, 0) + amount
9
- data.save_money()
9
+ data.save_toml(data.money, data.get_data_path("money.toml"))
10
10
  balance = data.money["members"][username]
11
11
  return (
12
12
  choice(data.output["money"]["add_money"])
@@ -21,7 +21,7 @@ def remove_money(username, amount):
21
21
  current = data.money["members"].get(username, 0)
22
22
  if current < amount:
23
23
  data.money["members"][username] = 0
24
- data.save_money()
24
+ data.save_toml(data.money, data.get_data_path("money.toml"))
25
25
  return (
26
26
  choice(data.output["money"]["insufficient_funds"])
27
27
  .replace("{{USERNAME}}", username)
@@ -30,7 +30,7 @@ def remove_money(username, amount):
30
30
  )
31
31
  else:
32
32
  data.money["members"][username] -= amount
33
- data.save_money()
33
+ data.save_toml(data.money, data.get_data_path("money.toml"))
34
34
  balance = data.money["members"][username]
35
35
  return (
36
36
  choice(data.output["money"]["remove_money"])
@@ -46,7 +46,7 @@ def check_balance(username):
46
46
  balance = data.money["members"][username]
47
47
  except (KeyError, tomlkit.exceptions.NonExistentKey):
48
48
  data.money["members"][username] = 0
49
- data.save_money()
49
+ data.save_toml(data.money, data.get_data_path("money.toml"))
50
50
  balance = 0
51
51
  return (
52
52
  choice(data.output["money"]["check_balance"])
@@ -0,0 +1,180 @@
1
+ import json
2
+ import os
3
+
4
+ import appdirs
5
+ import InquirerPy.utils
6
+ import tomlkit
7
+ from InquirerPy.base.control import Choice
8
+ from InquirerPy.prompts import checkbox, confirm, input, secret
9
+ from InquirerPy.prompts import list as inquirer_list
10
+
11
+ from .data import config
12
+
13
+ ACCENT = "\033[0;36m"
14
+ GRAY = "\u001b[0;37m"
15
+ RESET = "\033[0m"
16
+
17
+ style = InquirerPy.utils.InquirerPyStyle({"questionmark": "white"})
18
+
19
+
20
+ def onboarding():
21
+ from . import templates
22
+
23
+ active_data = tomlkit.loads(templates.active)
24
+ all_commands = list(active_data.keys())
25
+ active_choices = [Choice(value, enabled=True) for value in all_commands]
26
+
27
+ print(f"\n{ACCENT}Welcome to Comprobot{RESET}")
28
+ print("Thank you so much for downloading this bot!")
29
+ print("I am going to guide you through the process of setting it up.")
30
+ print(f"- First, go to {ACCENT}discord.com/developers/applications{RESET}.")
31
+ print("- Create a new application.")
32
+ print(
33
+ f"- Under the {ACCENT}Bot{RESET} section, you can customize the bot's name, avatar and banner."
34
+ )
35
+ print(f"- Next, head to the {ACCENT}OAuth2{RESET} section.")
36
+ print(
37
+ f"- Under {ACCENT}OAuth2 URL Generator{RESET}, select only the {ACCENT}bot{RESET} option."
38
+ )
39
+ print(
40
+ f"- Then, scroll down to {ACCENT}Bot Permissions{RESET} and select {ACCENT}Administrator{RESET}."
41
+ )
42
+ print(
43
+ f"- Copy the link and open it in a new tab. Select your server, then click {ACCENT}Continue{RESET} and {ACCENT}Authorize{RESET}"
44
+ )
45
+ print(
46
+ f"- Back on the {ACCENT}Application dashboard{RESET}, head back to the {ACCENT}Bot{RESET} section."
47
+ )
48
+ print(
49
+ f"- Click on {ACCENT}Reset Token{RESET} and then on {ACCENT}Yes, do it!{RESET}."
50
+ )
51
+ print("- Copy the new token and paste it here.")
52
+ print()
53
+
54
+ try:
55
+ # 1. Bot Token
56
+ token = secret.SecretPrompt(
57
+ message="Your bot token:",
58
+ style=style,
59
+ vi_mode=True,
60
+ ).execute()
61
+
62
+ print()
63
+
64
+ # 2. Commands to activate
65
+ commands_activated = checkbox.CheckboxPrompt(
66
+ message="Select commands to activate:",
67
+ instruction="Press Space to deselect and press Enter to continue.",
68
+ choices=active_choices,
69
+ style=style,
70
+ transformer=lambda result: (
71
+ f"{len(result)} commands selected" if result else "No commands selected"
72
+ ),
73
+ show_cursor=False,
74
+ ).execute()
75
+
76
+ print()
77
+
78
+ # 3. Activate AI features
79
+ ai_activated = confirm.ConfirmPrompt(
80
+ message="Do you want to activate AI features?", style=style, default=True
81
+ ).execute()
82
+ if ai_activated:
83
+ print()
84
+
85
+ # 3.1. Select provider
86
+ provider = inquirer_list.ListPrompt(
87
+ message="Select a provider:",
88
+ choices=[
89
+ Choice(value="groq", name="Groq (recommended)"),
90
+ Choice(value="gemini", name="Gemini"),
91
+ Choice(value="ollama", name="Ollama (local)"),
92
+ ],
93
+ default="groq",
94
+ style=style,
95
+ show_cursor=False,
96
+ ).execute()
97
+
98
+ match provider:
99
+ case "groq":
100
+ print()
101
+ print(
102
+ f"To get your forever-free Groq API key, head to {ACCENT}console.groq.com/keys{RESET}."
103
+ )
104
+ print(
105
+ "Create an account and then create a new API key without an expiration date."
106
+ )
107
+ print("Then copy the API key and paste it here.")
108
+ case "gemini":
109
+ print()
110
+ pass
111
+ case "ollama":
112
+ pass
113
+ case _:
114
+ ai_activated = False
115
+ provider = None
116
+ api_key = None
117
+
118
+ if provider in ["groq", "gemini"]:
119
+ print()
120
+
121
+ # 3.2. Get API key
122
+ api_key = secret.SecretPrompt(
123
+ message="Your API key:",
124
+ style=style,
125
+ vi_mode=True,
126
+ ).execute()
127
+ else:
128
+ api_key = None
129
+ print()
130
+
131
+ # 3.3. Select model
132
+ model = input.InputPrompt(
133
+ message="Enter the model you want to use:",
134
+ style=style,
135
+ vi_mode=True,
136
+ ).execute()
137
+
138
+ else:
139
+ model = None
140
+ provider = None
141
+ api_key = None
142
+
143
+ print()
144
+
145
+ except KeyboardInterrupt:
146
+ quit()
147
+
148
+ env_path = os.path.join(
149
+ appdirs.user_data_dir(appname="Comprobot", appauthor=False), ".env"
150
+ )
151
+ with open(env_path, "w") as f:
152
+ f.write(f"BOT_TOKEN={token}\n")
153
+ if api_key:
154
+ if provider == "groq":
155
+ f.write(f"GROQ={api_key}\n")
156
+ f.write("GEMINI=\n")
157
+ elif provider == "gemini":
158
+ f.write(f"GEMINI={api_key}\n")
159
+ f.write("GROQ=\n")
160
+ else:
161
+ f.write("GROQ=\n")
162
+ f.write("GEMINI=\n")
163
+
164
+ result = {
165
+ "token": token,
166
+ "commands_activated": commands_activated,
167
+ "ai_activated": ai_activated,
168
+ "provider": provider,
169
+ "api_key": api_key,
170
+ "model": model,
171
+ }
172
+
173
+ if config["debug_mode"]:
174
+ print()
175
+ print(json.dumps(result, indent=4))
176
+ print()
177
+
178
+ print(f"{ACCENT}You're all set!{RESET} Have fun using Comprobot!")
179
+
180
+ return result
@@ -118,20 +118,11 @@ mute = []
118
118
  time_to_mute = 5 # In minutes
119
119
  """
120
120
 
121
- env_template = r"""BOT_TOKEN=
121
+ env = r"""BOT_TOKEN=
122
122
  GEMINI=
123
123
  GROQ=
124
124
  """
125
125
 
126
- create_commands = r"""create("config.toml", config)
127
- create("error-messages.toml", error_messages)
128
- create("keywords.toml", keywords)
129
- create("ai.toml", ai)
130
- create("moderation.toml", moderation)
131
- create("data/.do_not_touch/money.toml", money)
132
- create("data/.do_not_touch/conversation_history.toml", conversation_history)
133
- create(".env", env_template)"""
134
-
135
126
  active = r"""quote = true
136
127
  joke = true
137
128
  meme = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comprobot
3
- Version: 2.0.4
3
+ Version: 2.1.0
4
4
  Summary: A self-hostable Discord bot built for maximum customization.
5
5
  Author: badluma
6
6
  License: MIT
@@ -19,10 +19,11 @@ Requires-Dist: appdirs
19
19
  Requires-Dist: ollama
20
20
  Requires-Dist: google-genai
21
21
  Requires-Dist: groq
22
+ Requires-Dist: InqurererPy
22
23
  Provides-Extra: dev
23
24
  Requires-Dist: ruff; extra == "dev"
24
25
 
25
- ## Description
26
+ # Comprobot
26
27
 
27
28
  Comprobot is a highly-customizable, open-source Discord bot that you can run on your own server.
28
29
 
@@ -1,4 +1,4 @@
1
- ## Description
1
+ # Comprobot
2
2
 
3
3
  Comprobot is a highly-customizable, open-source Discord bot that you can run on your own server.
4
4
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comprobot
3
- Version: 2.0.4
3
+ Version: 2.1.0
4
4
  Summary: A self-hostable Discord bot built for maximum customization.
5
5
  Author: badluma
6
6
  License: MIT
@@ -19,10 +19,11 @@ Requires-Dist: appdirs
19
19
  Requires-Dist: ollama
20
20
  Requires-Dist: google-genai
21
21
  Requires-Dist: groq
22
+ Requires-Dist: InqurererPy
22
23
  Provides-Extra: dev
23
24
  Requires-Dist: ruff; extra == "dev"
24
25
 
25
- ## Description
26
+ # Comprobot
26
27
 
27
28
  Comprobot is a highly-customizable, open-source Discord bot that you can run on your own server.
28
29
 
@@ -10,6 +10,7 @@ Comprobot/functions.py
10
10
  Comprobot/main.py
11
11
  Comprobot/moderation.py
12
12
  Comprobot/money_system.py
13
+ Comprobot/onboarding.py
13
14
  Comprobot/process.py
14
15
  Comprobot/templates.py
15
16
  comprobot.egg-info/PKG-INFO
@@ -6,6 +6,7 @@ appdirs
6
6
  ollama
7
7
  google-genai
8
8
  groq
9
+ InqurererPy
9
10
 
10
11
  [dev]
11
12
  ruff
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "comprobot"
7
- version = "2.0.4"
7
+ version = "2.1.0"
8
8
  authors = [{name = "badluma"}]
9
9
  description = "A self-hostable Discord bot built for maximum customization."
10
10
  readme = "README.md"
@@ -24,6 +24,7 @@ dependencies = [
24
24
  "ollama",
25
25
  "google-genai",
26
26
  "groq",
27
+ "InqurererPy",
27
28
  ]
28
29
 
29
30
  [project.optional-dependencies]
@@ -1,4 +0,0 @@
1
- from .main import main
2
-
3
- if __name__ == "__main__":
4
- main()
@@ -1,84 +0,0 @@
1
- import os
2
- from typing import Any, Dict, List
3
-
4
- import appdirs
5
- import tomlkit
6
-
7
- from . import templates
8
-
9
-
10
- def _get_data_path(filename):
11
-
12
- base_dir = appdirs.user_data_dir(appname="Comprobot", appauthor=False)
13
- return os.path.join(base_dir, filename)
14
-
15
-
16
- def _ensure_file(path, content):
17
-
18
- os.makedirs(os.path.dirname(path), exist_ok=True)
19
- if not os.path.isfile(path):
20
- with open(path, "w") as f:
21
- f.write(content)
22
-
23
-
24
- def _merge_defaults(data, defaults):
25
- for key, value in defaults.items():
26
- if key not in data:
27
- data[key] = value
28
- elif isinstance(value, dict) and isinstance(data.get(key), dict):
29
- _merge_defaults(data[key], value)
30
-
31
-
32
- def _load_or_create(path, template_content):
33
- try:
34
- with open(path, "rb") as f:
35
- data = tomlkit.load(f)
36
- except FileNotFoundError:
37
- _ensure_file(path, template_content)
38
- with open(path, "rb") as f:
39
- data = tomlkit.load(f)
40
-
41
- defaults = tomlkit.loads(template_content)
42
- _merge_defaults(data, defaults)
43
-
44
- if data != defaults:
45
- with open(path, "w") as f:
46
- tomlkit.dump(data, f)
47
-
48
- return data
49
-
50
-
51
- error_messages: Dict[str, str] = _load_or_create(
52
- _get_data_path("error-messages.toml"), templates.error_messages
53
- )
54
- config: Dict[str, Any] = _load_or_create(
55
- _get_data_path("config.toml"), templates.config
56
- )
57
- keywords: Dict[str, Dict[str, List[str]]] = _load_or_create(
58
- _get_data_path("keywords.toml"), templates.keywords
59
- )
60
- ai: Dict[str, Any] = _load_or_create(_get_data_path("ai.toml"), templates.ai)
61
- system_prompt_text = ai["system_prompt"]
62
- money: Dict[str, Dict[str, int]] = _load_or_create(
63
- _get_data_path("money.toml"), r"""members = {}"""
64
- )
65
- active: Dict[str, bool] = _load_or_create(
66
- _get_data_path("active.toml"), templates.active
67
- )
68
- output: Dict[str, Dict[str, List[str]]] = _load_or_create(
69
- _get_data_path("output.toml"), templates.output
70
- )
71
- moderation: Dict[Any, Any] = _load_or_create(
72
- _get_data_path("moderation.toml"), templates.moderation
73
- )
74
-
75
- _ensure_file(_get_data_path(".env"), templates.env_template)
76
-
77
-
78
- def save_toml(data, path):
79
- with open(path, "w") as f:
80
- tomlkit.dump(data, f)
81
-
82
-
83
- def save_money():
84
- save_toml(money, _get_data_path("money.toml"))
File without changes
File without changes
File without changes