comprobot 2.1.9__tar.gz → 2.2.3__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 (27) hide show
  1. {comprobot-2.1.9 → comprobot-2.2.3}/PKG-INFO +16 -1
  2. {comprobot-2.1.9 → comprobot-2.2.3}/README.md +15 -0
  3. {comprobot-2.1.9 → comprobot-2.2.3}/comprobot.egg-info/PKG-INFO +16 -1
  4. {comprobot-2.1.9 → comprobot-2.2.3}/comprobot.egg-info/SOURCES.txt +2 -1
  5. comprobot-2.2.3/comprobot.egg-info/entry_points.txt +2 -0
  6. {comprobot-2.1.9 → comprobot-2.2.3}/pyproject.toml +2 -2
  7. {comprobot-2.1.9 → comprobot-2.2.3}/src/__main__.py +27 -17
  8. comprobot-2.2.3/src/config.py +221 -0
  9. {comprobot-2.1.9 → comprobot-2.2.3}/src/moderation.py +2 -0
  10. {comprobot-2.1.9 → comprobot-2.2.3}/src/onboarding.py +6 -1
  11. comprobot-2.1.9/src/main.py → comprobot-2.2.3/src/start.py +1 -1
  12. comprobot-2.1.9/comprobot.egg-info/entry_points.txt +0 -2
  13. {comprobot-2.1.9 → comprobot-2.2.3}/LICENSE +0 -0
  14. {comprobot-2.1.9 → comprobot-2.2.3}/comprobot.egg-info/dependency_links.txt +0 -0
  15. {comprobot-2.1.9 → comprobot-2.2.3}/comprobot.egg-info/requires.txt +0 -0
  16. {comprobot-2.1.9 → comprobot-2.2.3}/comprobot.egg-info/top_level.txt +0 -0
  17. {comprobot-2.1.9 → comprobot-2.2.3}/setup.cfg +0 -0
  18. {comprobot-2.1.9 → comprobot-2.2.3}/src/__init__.py +0 -0
  19. {comprobot-2.1.9 → comprobot-2.2.3}/src/api.py +0 -0
  20. {comprobot-2.1.9 → comprobot-2.2.3}/src/bot.py +0 -0
  21. {comprobot-2.1.9 → comprobot-2.2.3}/src/commands.py +0 -0
  22. {comprobot-2.1.9 → comprobot-2.2.3}/src/data.py +0 -0
  23. {comprobot-2.1.9 → comprobot-2.2.3}/src/functions.py +0 -0
  24. {comprobot-2.1.9 → comprobot-2.2.3}/src/money_system.py +0 -0
  25. {comprobot-2.1.9 → comprobot-2.2.3}/src/process.py +0 -0
  26. {comprobot-2.1.9 → comprobot-2.2.3}/src/templates.py +0 -0
  27. {comprobot-2.1.9 → comprobot-2.2.3}/src/testing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comprobot
3
- Version: 2.1.9
3
+ Version: 2.2.3
4
4
  Summary: A self-hostable Discord bot built for maximum customization.
5
5
  Author: badluma
6
6
  License: MIT
@@ -66,6 +66,21 @@ scoop bucket add badluma https://github.com/badluma/scoop-bucket
66
66
  scoop install comprobot
67
67
  ```
68
68
 
69
+ **Docker**
70
+ ```sh
71
+ docker run -d \
72
+ -v comprobot-data:/root/.local/share/Comprobot \
73
+ --name comprobot \
74
+ badluma/comprobot:latest
75
+ ```
76
+
77
+ First run, set up credentials:
78
+ ```sh
79
+ docker run -it --rm \
80
+ -v comprobot-data:/root/.local/share/Comprobot \
81
+ badluma/comprobot:latest onboard
82
+ ```
83
+
69
84
  ## Documentation
70
85
 
71
86
  You can find the whole documentation [here](https://badluma.github.io/Comprobot-Docs/).
@@ -39,6 +39,21 @@ scoop bucket add badluma https://github.com/badluma/scoop-bucket
39
39
  scoop install comprobot
40
40
  ```
41
41
 
42
+ **Docker**
43
+ ```sh
44
+ docker run -d \
45
+ -v comprobot-data:/root/.local/share/Comprobot \
46
+ --name comprobot \
47
+ badluma/comprobot:latest
48
+ ```
49
+
50
+ First run, set up credentials:
51
+ ```sh
52
+ docker run -it --rm \
53
+ -v comprobot-data:/root/.local/share/Comprobot \
54
+ badluma/comprobot:latest onboard
55
+ ```
56
+
42
57
  ## Documentation
43
58
 
44
59
  You can find the whole documentation [here](https://badluma.github.io/Comprobot-Docs/).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comprobot
3
- Version: 2.1.9
3
+ Version: 2.2.3
4
4
  Summary: A self-hostable Discord bot built for maximum customization.
5
5
  Author: badluma
6
6
  License: MIT
@@ -66,6 +66,21 @@ scoop bucket add badluma https://github.com/badluma/scoop-bucket
66
66
  scoop install comprobot
67
67
  ```
68
68
 
69
+ **Docker**
70
+ ```sh
71
+ docker run -d \
72
+ -v comprobot-data:/root/.local/share/Comprobot \
73
+ --name comprobot \
74
+ badluma/comprobot:latest
75
+ ```
76
+
77
+ First run, set up credentials:
78
+ ```sh
79
+ docker run -it --rm \
80
+ -v comprobot-data:/root/.local/share/Comprobot \
81
+ badluma/comprobot:latest onboard
82
+ ```
83
+
69
84
  ## Documentation
70
85
 
71
86
  You can find the whole documentation [here](https://badluma.github.io/Comprobot-Docs/).
@@ -12,12 +12,13 @@ src/__main__.py
12
12
  src/api.py
13
13
  src/bot.py
14
14
  src/commands.py
15
+ src/config.py
15
16
  src/data.py
16
17
  src/functions.py
17
- src/main.py
18
18
  src/moderation.py
19
19
  src/money_system.py
20
20
  src/onboarding.py
21
21
  src/process.py
22
+ src/start.py
22
23
  src/templates.py
23
24
  src/testing.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ comprobot = src.__main__:main
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "comprobot"
7
- version = "2.1.9"
7
+ version = "2.2.3"
8
8
  authors = [{name = "badluma"}]
9
9
  description = "A self-hostable Discord bot built for maximum customization."
10
10
  readme = "README.md"
@@ -35,7 +35,7 @@ Homepage = "https://badluma.github.io/Comprobot-Docs"
35
35
  Repository = "https://github.com/badluma/comprobot"
36
36
 
37
37
  [project.scripts]
38
- comprobot = "src.__main__:cli_main"
38
+ comprobot = "src.__main__:main"
39
39
 
40
40
  [tool.setuptools.packages.find]
41
41
  where = ["."]
@@ -1,33 +1,40 @@
1
1
  import argparse
2
2
 
3
- import dotenv
3
+ from dotenv import set_key as dotenv_set_key
4
4
 
5
5
  from .data import active, ai, get_data_path, save_toml
6
- from .main import main
6
+ from .start import start
7
7
  from .onboarding import onboarding
8
+ from .config import configure
8
9
 
9
10
 
10
- def cli_main():
11
+ def main():
11
12
  parser = argparse.ArgumentParser(
12
13
  prog="comprobot",
13
14
  description="A self-hostable open-source Discord bot built for maximum customization.",
14
15
  )
15
16
  subparsers = parser.add_subparsers(dest="command", metavar="")
16
- subparsers.add_parser("onboard", help="Set up Comprobot for the first time.")
17
17
  subparsers.add_parser("start", help="Start the bot.")
18
+ subparsers.add_parser("onboard", help="Set up Comprobot for the first time.")
19
+ config_parser = subparsers.add_parser("config", help="Configure the bot's settings.")
20
+ config_parser.add_argument("config_args", nargs=argparse.REMAINDER)
18
21
  test_parser = subparsers.add_parser("test", help="Process a message through the bot's command processor.")
19
22
  test_parser.add_argument("message", help="The message to process (e.g. '!calculate 2+2')")
20
23
 
21
24
  args = parser.parse_args()
22
25
 
26
+ print(args)
27
+
23
28
  match args.command:
24
29
  case "start":
25
- main()
30
+ start()
26
31
  case "onboard":
27
32
  settings = onboarding()
28
33
 
29
- dotenv.set_key(
30
- dotenv_path=".env",
34
+ env_path = get_data_path(".env")
35
+
36
+ dotenv_set_key(
37
+ dotenv_path=env_path,
31
38
  key_to_set="BOT_TOKEN",
32
39
  value_to_set=str(settings["token"]),
33
40
  )
@@ -43,24 +50,24 @@ def cli_main():
43
50
  ai["provider"] = settings["provider"]
44
51
 
45
52
  if settings["provider"] == "groq":
46
- dotenv.set_key(
47
- dotenv_path=".env",
53
+ dotenv_set_key(
54
+ dotenv_path=env_path,
48
55
  key_to_set="GROQ",
49
56
  value_to_set=settings["api_key"],
50
57
  )
51
- dotenv.set_key(dotenv_path=".env", key_to_set="GEMINI", value_to_set="")
58
+ dotenv_set_key(dotenv_path=env_path, key_to_set="GEMINI", value_to_set="")
52
59
 
53
60
  elif settings["provider"] == "gemini":
54
- dotenv.set_key(
55
- dotenv_path=".env",
61
+ dotenv_set_key(
62
+ dotenv_path=env_path,
56
63
  key_to_set="GEMINI",
57
64
  value_to_set=settings["api_key"],
58
65
  )
59
- dotenv.set_key(dotenv_path=".env", key_to_set="GROQ", value_to_set="")
66
+ dotenv_set_key(dotenv_path=env_path, key_to_set="GROQ", value_to_set="")
60
67
 
61
68
  else:
62
- dotenv.set_key(dotenv_path=".env", key_to_set="GROQ", value_to_set="")
63
- dotenv.set_key(dotenv_path=".env", key_to_set="GEMINI", value_to_set="")
69
+ dotenv_set_key(dotenv_path=env_path, key_to_set="GROQ", value_to_set="")
70
+ dotenv_set_key(dotenv_path=env_path, key_to_set="GEMINI", value_to_set="")
64
71
 
65
72
  if settings["model"]:
66
73
  ai["model"] = settings["model"]
@@ -70,7 +77,10 @@ def cli_main():
70
77
  save_toml(ai, get_data_path("ai.toml"))
71
78
  save_toml(active, get_data_path("active.toml"))
72
79
 
73
- main()
80
+ start()
81
+
82
+ case "config":
83
+ configure(args.config_args)
74
84
 
75
85
  case "test":
76
86
  from .testing import run_test
@@ -81,4 +91,4 @@ def cli_main():
81
91
 
82
92
 
83
93
  if __name__ == "__main__":
84
- cli_main()
94
+ main()
@@ -0,0 +1,221 @@
1
+ import sys
2
+ import os
3
+
4
+ import appdirs
5
+ import tomlkit
6
+
7
+ import InquirerPy.utils
8
+ from InquirerPy.base.control import Choice
9
+ from InquirerPy.prompts import checkbox, confirm, input, secret
10
+ from InquirerPy.prompts import list as inquirer_list
11
+ from tomlkit.items import Key
12
+
13
+ style = InquirerPy.utils.InquirerPyStyle({
14
+ "instruction": "#aaaaaa italic",
15
+ "questionmark": "#5865F2 bold",
16
+ "answermark": "#5865F2",
17
+ "answer": "#5865F2",
18
+ "question": "#ffffff",
19
+ })
20
+
21
+ def make_pretty(string):
22
+ return string \
23
+ .replace(".toml", "") \
24
+ .replace("_", " ") \
25
+ .replace("-", " ") \
26
+ .title() \
27
+ .replace("Ai", "AI")
28
+
29
+ def pick_file():
30
+ data_dir = appdirs.user_data_dir("Comprobot", appauthor=False)
31
+
32
+ files = {}
33
+
34
+ for file in os.listdir(data_dir):
35
+ if not file.endswith(".toml"):
36
+ continue
37
+ files[file] = {}
38
+ files[file]["path"] = os.path.join(data_dir, file)
39
+ files[file]["display"] = make_pretty(file)
40
+
41
+ if not files:
42
+ print("No configuration files found.")
43
+ sys.exit(1)
44
+
45
+ env_path = os.path.join(appdirs.user_data_dir("Comprobot", appauthor=False), ".env")
46
+
47
+ file_to_edit = inquirer_list.ListPrompt(
48
+ message="Which file do you want to edit?",
49
+ choices=[Choice(value=files[file]["path"], name=files[file]["display"]) for file in files]
50
+ + ([Choice(value=env_path, name="Secrets")] if os.path.exists(env_path) else [])
51
+ + [Choice(value="exit", name="Exit")],
52
+ style=style,
53
+ amark="!",
54
+ vi_mode=True,
55
+ show_cursor=False,
56
+ ).execute()
57
+
58
+ if file_to_edit == "exit":
59
+ sys.exit(0)
60
+
61
+ if file_to_edit == env_path:
62
+ from dotenv import dotenv_values
63
+ content = {k: v or "" for k, v in dotenv_values(file_to_edit).items()}
64
+ return (file_to_edit, content)
65
+
66
+ with open(file_to_edit, "r") as f:
67
+ content = tomlkit.load(f)
68
+
69
+ return (file_to_edit, content)
70
+
71
+
72
+ def pick_key(content, is_secret=False):
73
+
74
+ print()
75
+
76
+ key = inquirer_list.ListPrompt(
77
+ message="Which key do you want to edit?",
78
+ choices=[Choice(value=key, name=make_pretty(key)) for key in list(content.keys())]
79
+ + [Choice(value="exit", name="Exit")],
80
+ style=style,
81
+ amark="!",
82
+ show_cursor=False,
83
+ vi_mode=True,
84
+ ).execute()
85
+
86
+ if key == "exit":
87
+ sys.exit(0)
88
+
89
+ match content[key]:
90
+ case dict():
91
+ pick_key(content[key], is_secret=is_secret)
92
+
93
+ case bool():
94
+ print()
95
+ value = confirm.ConfirmPrompt(
96
+ message=f"Do you want to set '{make_pretty(key)}' to True or False?",
97
+ instruction=f"(y/n) (Current: {content[key]})",
98
+ style=style,
99
+ amark="!",
100
+ vi_mode=True,
101
+ ).execute()
102
+ content[key] = value
103
+
104
+ case str() | int():
105
+ print()
106
+ if is_secret:
107
+ value = secret.SecretPrompt(
108
+ message=f"New value for '{make_pretty(key)}'?",
109
+ style=style,
110
+ amark="!",
111
+ vi_mode=True,
112
+ ).execute()
113
+ else:
114
+ value = input.InputPrompt(
115
+ message=f"What value do you want to assign to '{make_pretty(key)}'?",
116
+ instruction=f"(Current: {content[key]})",
117
+ style=style,
118
+ amark="!",
119
+ vi_mode=True,
120
+ ).execute()
121
+ content[key] = value
122
+
123
+ case list():
124
+ print()
125
+ value = input.InputPrompt(
126
+ message=f"What values do you want to assign to '{make_pretty(key)}'?",
127
+ instruction=f"(separate by commas) (Current: {', '.join(content[key]) if content[key] else 'None'})",
128
+ style=style,
129
+ amark="!",
130
+ vi_mode=True,
131
+ ).execute().split(",")
132
+ content[key] = [item.strip() for item in value]
133
+
134
+
135
+
136
+ def configure(args):
137
+
138
+ if not args:
139
+ print()
140
+ try:
141
+ file_to_edit, content = pick_file()
142
+ except KeyboardInterrupt:
143
+ sys.exit(0)
144
+ except Exception as e:
145
+ print(f"Error: {e}")
146
+ sys.exit(1)
147
+
148
+ is_secret = file_to_edit.endswith(".env")
149
+
150
+ while True:
151
+ try:
152
+ pick_key(content, is_secret=is_secret)
153
+ except KeyboardInterrupt:
154
+ sys.exit(0)
155
+ except Exception as e:
156
+ print(f"Error: {e}")
157
+ continue
158
+
159
+ if is_secret:
160
+ from dotenv import set_key as dotenv_set_key
161
+ for k, v in content.items():
162
+ dotenv_set_key(dotenv_path=file_to_edit, key_to_set=k, value_to_set=v)
163
+ else:
164
+ with open(file_to_edit, "w") as f:
165
+ tomlkit.dump(content, f)
166
+ else:
167
+ if len(args) < 3:
168
+ print("Usage: config <file> <key> <value(s)>")
169
+ sys.exit(1)
170
+
171
+ file_name, key, *values = args
172
+
173
+ data_dir = appdirs.user_data_dir("Comprobot", appauthor=False)
174
+
175
+ if file_name == "secrets":
176
+ from dotenv import set_key as dotenv_set_key
177
+ env_path = os.path.join(data_dir, ".env")
178
+ if not os.path.exists(env_path):
179
+ print(".env file not found.")
180
+ sys.exit(1)
181
+ if not values:
182
+ print("Usage: config secrets <key> <value>")
183
+ sys.exit(1)
184
+ dotenv_set_key(dotenv_path=env_path, key_to_set=key, value_to_set=values[0])
185
+ return
186
+
187
+ file_path = os.path.join(data_dir, f"{file_name}.toml")
188
+
189
+ if not os.path.exists(file_path):
190
+ print(f"File '{file_name}.toml' not found.")
191
+ sys.exit(1)
192
+
193
+ with open(file_path, "r") as f:
194
+ content = tomlkit.load(f)
195
+
196
+ if key not in content:
197
+ print(f"Key '{key}' not found in {file_name}.toml.")
198
+ sys.exit(1)
199
+
200
+ current = content[key]
201
+
202
+ match current:
203
+ case bool():
204
+ v = values[0].lower()
205
+ if v not in ("true", "false"):
206
+ print(f"Invalid value '{values[0]}' for bool. Use 'true' or 'false'.")
207
+ sys.exit(1)
208
+ content[key] = v == "true"
209
+ case list():
210
+ content[key] = values
211
+ case str():
212
+ content[key] = values[0]
213
+ case int():
214
+ try:
215
+ content[key] = int(values[0])
216
+ except ValueError:
217
+ print(f"Invalid value '{values[0]}' for int key.")
218
+ sys.exit(1)
219
+
220
+ with open(file_path, "w") as f:
221
+ tomlkit.dump(content, f)
@@ -15,6 +15,7 @@ async def ban(message, item):
15
15
  member = await message.guild.fetch_member(message.author.id)
16
16
  except discord.NotFound:
17
17
  print(f"Could not fetch member {message.author.name}.")
18
+ return
18
19
 
19
20
  try:
20
21
  try:
@@ -36,6 +37,7 @@ async def kick(message, item):
36
37
  member = await message.guild.fetch_member(message.author.id)
37
38
  except discord.NotFound:
38
39
  print(f"Could not fetch member {message.author.name}.")
40
+ return
39
41
 
40
42
  try:
41
43
  try:
@@ -56,6 +56,7 @@ def onboarding():
56
56
  token = secret.SecretPrompt(
57
57
  message="Your bot token:",
58
58
  style=style,
59
+ amark="!",
59
60
  vi_mode=True,
60
61
  ).execute()
61
62
 
@@ -67,6 +68,7 @@ def onboarding():
67
68
  instruction="Press Space to deselect and press Enter to continue.",
68
69
  choices=active_choices,
69
70
  style=style,
71
+ amark="!",
70
72
  transformer=lambda result: (
71
73
  f"{len(result)} commands selected" if result else "No commands selected"
72
74
  ),
@@ -77,7 +79,7 @@ def onboarding():
77
79
 
78
80
  # 3. Activate AI features
79
81
  ai_activated = confirm.ConfirmPrompt(
80
- message="Do you want to activate AI features?", style=style, default=True
82
+ message="Do you want to activate AI features?", style=style, amark="!", default=True
81
83
  ).execute()
82
84
  if ai_activated:
83
85
  print()
@@ -92,6 +94,7 @@ def onboarding():
92
94
  ],
93
95
  default="groq",
94
96
  style=style,
97
+ amark="!",
95
98
  show_cursor=False,
96
99
  ).execute()
97
100
 
@@ -122,6 +125,7 @@ def onboarding():
122
125
  api_key = secret.SecretPrompt(
123
126
  message="Your API key:",
124
127
  style=style,
128
+ amark="!",
125
129
  vi_mode=True,
126
130
  ).execute()
127
131
  else:
@@ -132,6 +136,7 @@ def onboarding():
132
136
  model = input.InputPrompt(
133
137
  message="Enter the model you want to use:",
134
138
  style=style,
139
+ amark="!",
135
140
  vi_mode=True,
136
141
  ).execute()
137
142
 
@@ -40,7 +40,7 @@ async def on_ready():
40
40
  para()
41
41
 
42
42
 
43
- def main():
43
+ def start():
44
44
  print(
45
45
  f"Configuration directory: {appdirs.user_data_dir(appname='Comprobot', appauthor=False)}"
46
46
  )
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- comprobot = src.__main__:cli_main
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes