Skip to content
This repository was archived by the owner on Oct 5, 2025. It is now read-only.

Commit 79dd580

Browse files
authored
Reworked Pymon to Use a SQLite Database on the Backend (#21)
* Updated requirements file * Reworked directory structure * Removed unused intents * Performed some light formatting * Building out brain * Fully developed brain layout * Added the brain to pymon * Removed references to client * Changed code to use FTS5 * Removed logs * Migrated authors * Author ID mapping works * Code works! * Working through database stuff * Remove file * Fully migrated old data * Added resources and authors * Reworked search to return exactly what I want * React on mention is up to speed * Removed old functions * Removed refresh command * Fixed up the get command * Fixed up the tags issue * Cleaned up brain code * Patched up all existing features * Cleaned up code * Removed old tests * Cleaning up existing files * Reworked readme code * Added the new query feature * Added missing comments * Fixed an issue with the new command * Added two more queries * Added one more query * Removed python workflow
1 parent ae4b390 commit 79dd580

File tree

17 files changed

+680
-1028
lines changed

17 files changed

+680
-1028
lines changed

.env.test_type_0

Lines changed: 0 additions & 1 deletion
This file was deleted.

.env.test_type_1

Lines changed: 0 additions & 1 deletion
This file was deleted.

.github/workflows/python-app.yml

Lines changed: 0 additions & 36 deletions
This file was deleted.

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,9 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
131+
# Database files
132+
*.db
133+
134+
# Logs
135+
/logs

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ Currently, the bot can answer the following questions. The numbers map directly
4040
2. "What is a primitive type?" by Jeremy Grifski
4141
3. "How can I create a constant?" by Jeremy Grifski
4242
4. "What is testing?" by Jeremy Grifski
43-
5. "What is debugging?" by Jeremy Grifski, Xining Feng
43+
5. "What is debugging?" by Xining Feng, Jeremy Grifski
4444
6. "What is correctness?" by Jeremy Grifski
4545
7. "What is JUnit?" by Jeremy Grifski
4646
8. "What is unit testing?" by Jeremy Grifski
4747
9. "What is an expression tree?" by Jeremy Grifski
4848
10. "What is confidence building?" by Jeremy Grifski
4949
11. "What is the declared type of a variable?" by Jeremy Grifski
5050
12. "What is the static type of a variable?" by Jeremy Grifski
51-
13. "What is method overriding?" by Jeremy Grifski, Le Chang, Zihe Fang, Alexia Scarvelli, Tae Yeon Kim
51+
13. "What is method overriding?" by Jeremy Grifski, Alexia Scarvelli, Zihe Fang, Tae Yeon Kim, Le Chang
5252
14. "What is method overloading?" by Jeremy Grifski
53-
15. "What is the implements relationship?" by Jeremy Grifski, Yibo Gan
54-
16. "What is the extends relationship?" by Jeremy Grifski, Yi-You (Joseph) Chiu
53+
15. "What is the implements relationship?" by Yibo Gan, Jeremy Grifski
54+
16. "What is the extends relationship?" by Yi-You (Joseph) Chiu, Jeremy Grifski
5555
17. "What is a mathematical string?" by Jeremy Grifski
5656
18. "What is mathematical string notation for concatenation?" by Jeremy Grifski
5757
19. "What is a parameter mode?" by Jeremy Grifski
@@ -77,7 +77,7 @@ Currently, the bot can answer the following questions. The numbers map directly
7777
39. "What is the purpose of the queue data structure?" by Ying Liang
7878
40. "What is Recursion?" by Gani Sagiev, Tim Keck
7979
41. "What is the difference between unit and integration testing?" by Jacob Kolaczkowski
80-
42. "What is the difference between testing and debugging?" by John DiFilippo, Ahmed Mohamed
80+
42. "What is the difference between testing and debugging?" by Ahmed Mohamed, John DiFilippo
8181
43. "What is object-oriented programming (OOP)?" by Felix Ji
8282
44. "What type of method is a JUnit test case?" by Matthew Alfieri
8383
45. "What is aliasing?" by Yuhang Huang
@@ -89,7 +89,7 @@ Currently, the bot can answer the following questions. The numbers map directly
8989
51. "What is the benefit of interval halving?" by Ashir Faruq
9090
52. "What is mathematical induction?" by Om Amin
9191
53. "What is the Set data type?" by Luke Thompson
92-
54. "What is the correct expression to see if an integer is odd in Java?" by Kurt Wanner, Yi-You (Joseph) Chiu
92+
54. "What is the correct expression to see if an integer is odd in Java?" by Yi-You (Joseph) Chiu, Kurt Wanner
9393
55. "What is XML?" by Tim Keck
9494
56. "What is mutability?" by Tim Keck
9595
57. "What is a comparator?" by Tim Keck
@@ -119,6 +119,9 @@ Currently, the bot can answer the following questions. The numbers map directly
119119
81. "What is abstraction?" by Yingqi Gao
120120
82. "What is the difference between interface and abstract class?" by Yingqi Gao
121121
83. "When should I use newInstance?" by Shivam Engineer
122+
84. "What is an observer pattern?" by Jayshuk Pandrangi
123+
85. "What is a loop invariant?" by Jayshuk Pandrangi
124+
86. "What is MVC?" by Yuheng Long (Simon)
122125

123126
---
124127

pymon.py

Lines changed: 27 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,193 +1,40 @@
11
import logging
22
import os
33
import pathlib
4+
import sys
45
from logging.handlers import RotatingFileHandler
56

6-
import discord
7-
from discord import Message
8-
from discord.ext import commands
9-
from discord_slash import SlashCommand
10-
from discord_slash.utils.manage_commands import create_choice, create_option
7+
from dotenv import load_dotenv
118

12-
import pymon_utils as utils
9+
from pymon import VERSION, bot
1310

14-
__version__ = "0.6.0"
15-
16-
17-
# Setup logging
18-
logs_path = pathlib.Path(
19-
os.path.abspath(os.path.dirname(__file__)),
20-
"logs"
21-
)
22-
logs_path.mkdir(parents=True, exist_ok=True)
23-
logging.basicConfig(
24-
handlers=[RotatingFileHandler(
25-
logs_path / "bot.log",
11+
if __name__ == '__main__':
12+
# Setup handler
13+
log_path = pathlib.Path(os.path.abspath(os.path.dirname(__file__)), "logs")
14+
log_path.mkdir(exist_ok=True, parents=True)
15+
log_path = log_path / "pymon.log"
16+
file_handler = RotatingFileHandler(
17+
log_path,
2618
backupCount=10,
2719
maxBytes=1000000
28-
)],
29-
level=logging.DEBUG,
30-
format='%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
31-
datefmt='%Y-%m-%d:%H:%M:%S',
32-
)
33-
log = logging.getLogger(__name__)
34-
35-
# Global variables
36-
client = commands.Bot(
37-
command_prefix=commands.when_mentioned_or("!"),
38-
activity=discord.Activity(
39-
type=discord.ActivityType.listening,
40-
name='student questions'
41-
),
42-
status=discord.Status.idle
43-
)
44-
slash = SlashCommand(client, sync_commands=True)
45-
queries, keyword_mapping = utils.refresh_knowledge()
46-
47-
48-
# Discord bot code
49-
async def _react_on_mention(message: Message):
50-
if client.user.mentioned_in(message) and not message.mention_everyone:
51-
indices = utils.search(
52-
keyword_mapping, utils.generate_keywords(message.content))
53-
if indices:
54-
reply = list()
55-
reply.extend([
56-
f"{utils.create_md_link(queries[i].get('resource'), queries[i].get('query'))}"
57-
for i in indices[:3]
58-
])
59-
embed = discord.Embed(
60-
title=f"Pymon v{__version__}: Do any of these questions match your query?",
61-
description="Use the ID with the /get command to get an answer or follow the available links:",
62-
color=discord.Color.blue(),
63-
url="https://github.com/TheRenegadeCoder/pymon"
64-
)
65-
for idx, row in enumerate(reply):
66-
embed.add_field(
67-
name=f"#{idx + 1}: ID-{indices[idx]}",
68-
value=row,
69-
inline=True
70-
)
71-
embed.set_footer(
72-
text="Learn more about how this bot works by clicking the title.",
73-
icon_url="https://therenegadecoder.com/wp-content/uploads/2017/05/the-renegade-coder-icon-cropped.png"
74-
)
75-
await message.reply(embed=embed)
76-
else:
77-
await message.reply("How about we explore the area ahead of us later? https://tenor.com/bqnuv.gif")
78-
79-
80-
@client.event
81-
async def on_message(message: Message):
82-
"""
83-
The action to be called when a message is sent.
84-
85-
:param message: the message to scrutinize
86-
:return: None
87-
"""
88-
if message.author != client.user:
89-
await _react_on_mention(message)
90-
91-
92-
@slash.slash(
93-
name="get",
94-
description="Looks up the answer to a query by its ID.",
95-
options=[
96-
create_option(
97-
name="index",
98-
description="Select a question index",
99-
option_type=4,
100-
required=True
101-
)
102-
]
103-
)
104-
async def _get(ctx, index: int):
105-
"""
106-
Looks up the answer to a query by its ID.
107-
108-
:param ctx: the context to send messages to
109-
:return: None
110-
"""
111-
embed = discord.Embed(
112-
title=f"Pymon v{__version__}: Answer to ID-{index}",
113-
color=discord.Color.red(),
114-
url=queries[index].get("resource", discord.embeds.EmptyEmbed)
115-
)
116-
embed.add_field(
117-
name=queries[index].get("query"),
118-
value=queries[index].get("response"),
119-
inline=False
12020
)
121-
similar_queries = queries[index].get("similar_queries", [])[:3]
122-
if similar_queries:
123-
embed.add_field(
124-
name="Similar Queries",
125-
value="\n".join(
126-
f"• ID-{i}: {utils.create_md_link(queries[i].get('resource'), queries[i].get('query'))}"
127-
for i in similar_queries
128-
),
129-
inline=True
130-
)
131-
132-
await ctx.send(embed=embed)
133-
134-
135-
@slash.slash(
136-
name="study",
137-
description="Provides a study guide from a set of predetermined tags.",
138-
options=[
139-
create_option(
140-
name="tag",
141-
description="Select a topic for your study guide.",
142-
required=True,
143-
option_type=3,
144-
choices=[
145-
create_choice(
146-
value=tag,
147-
name=f"{tag} ({len(utils.get_queries_from_tag(queries, tag))})",
148-
)
149-
for tag in sorted(utils.generate_tags_set(queries))
150-
]
151-
)
152-
]
153-
)
154-
async def _study(ctx, tag: str):
155-
"""
156-
Prints out a list of questions relevant to the tag.
157-
158-
:param ctx: the context to send messages to
159-
:return: None
160-
"""
161-
matches = utils.get_queries_from_tag(queries, tag)
162-
embed = discord.Embed(
163-
title=f"Pymon v{__version__}: Study Guide for {tag}",
164-
color=discord.Color.red(),
165-
description="\n".join(
166-
f"• ID-{match[0]}: {utils.create_md_link(match[1].get('resource'), match[1].get('query'))}"
167-
for match in matches
168-
)
21+
console_handler = logging.StreamHandler(sys.stdout)
22+
23+
# Setup formatter
24+
msg_format = f'[Pymon {VERSION}] %(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s'
25+
date_format = '%Y-%m-%d:%H:%M:%S'
26+
27+
# Setup root logger
28+
logging.basicConfig(
29+
handlers=[file_handler, console_handler],
30+
level=logging.DEBUG,
31+
format=msg_format,
32+
datefmt=date_format,
16933
)
17034

171-
await ctx.send(embed=embed)
35+
# Load key environment variables
36+
load_dotenv()
17237

173-
174-
@slash.slash(
175-
name="refresh",
176-
description="Refreshes the bot's knowledge base.",
177-
)
178-
async def _refresh(ctx):
179-
"""
180-
Refreshes the bot's knowledge base.
181-
182-
:param ctx: the context to send messages to
183-
:return: None
184-
"""
185-
new_queries, new_keyword_mapping = utils.refresh_knowledge()
186-
global queries, keyword_mapping
187-
diff = [x for x in new_queries if x not in queries]
188-
queries, keyword_mapping = new_queries, new_keyword_mapping
189-
await ctx.send(f"{len(diff)} queries modified and/or added.")
190-
191-
192-
if __name__ == '__main__':
193-
client.run(os.environ.get("DISCORD_TOKEN"))
38+
# Initialize and run discord bot
39+
client = bot.Pymon()
40+
client.run(os.environ.get("DISCORD_TOKEN"), log_handler=None)

pymon/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VERSION = "1.0.0"

0 commit comments

Comments
 (0)