Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Pylint

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py')
10 changes: 5 additions & 5 deletions commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
default commands for the bot
"""
commands = {
'start': 'Hello there {}! I am a bot sent by the heavens to be yours. Type /help and know what I can do for you.',
'hi':'Hello there {}!',
'hello':'Hello there {}!',
'hey':'Hello there {}!',
'help': 'Hey, {}. I see you sought my help.\n\nI know the following commands:\n/Hi, /Hey, /Hello - They\'re pretty much the same😬\n/Help - To know how I can help you.\n\n/q followed by your query - regarding IIT Mandi. \ne.g:\n/q What all facilities are available in campus?\n\nFor General questions you can just ask straight-away.\ne.g:\nWhat is your name\nTell me a joke\n\nYou can also use /u, /d and /n to upvote, downvote and get the next answer respectively. These three are only valid when you ask a query.'
"start": "Hello there {}! I am a bot sent by the heavens to be yours. Type /help and know what I can do for you.",
"hi": "Hello there {}!",
"hello": "Hello there {}!",
"hey": "Hello there {}!",
"help": "Hey, {}. I see you sought my help.\n\nI know the following commands:\n/Hi, /Hey, /Hello - They're pretty much the same😬\n/Help - To know how I can help you.\n\n/q followed by your query - regarding IIT Mandi. \ne.g:\n/q What all facilities are available in campus?\n\nFor General questions you can just ask straight-away.\ne.g:\nWhat is your name\nTell me a joke\n\nYou can also use /u, /d and /n to upvote, downvote and get the next answer respectively. These three are only valid when you ask a query.",
}
10 changes: 8 additions & 2 deletions db.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

logger = logging.getLogger(__name__)


def init_db():
"""
Initializes the database client and returns a reference to the database.
Expand Down Expand Up @@ -112,12 +113,17 @@ def update_vote_qna(vote_type: str, answer: str):
"""
try:
qna = get_collection(DB, "qna")
where_clause = {'answers.answer': answer}
update_clause = {"$inc": {"answers.$.upvotes": 1}} if vote_type == "u" else {"$inc": {"answers.$.downvotes": 1}}
where_clause = {"answers.answer": answer}
update_clause = (
{"$inc": {"answers.$.upvotes": 1}}
if vote_type == "u"
else {"$inc": {"answers.$.downvotes": 1}}
)
qna.update_one(where_clause, update_clause)
except Exception as exception:
logger.exception("Error updating vote count for answer: %s", answer)
raise exception


global DB
DB = init_db()
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pymongo==4.3.3
pymongo==4.6.3
pyTelegramBotAPI==4.11.0
python-dotenv
tensorflow_hub
tensorflow
urllib3==1.26.6
urllib3==1.26.18
19 changes: 15 additions & 4 deletions resources/populateDB.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import sys
sys.path.append('..')

sys.path.append("..")
from db import *
from pprint import pprint


def restructureQnA():
collection = get_collection(DB, "qna")
all_documents = collection.find()
Expand All @@ -18,7 +20,11 @@ def restructureQnA():

# if the question is not in the dictionary, add it
if question not in question_answer_dict:
question_answer_dict[question] = {"_id": document["_id"], "question": question, "answers": [answer]}
question_answer_dict[question] = {
"_id": document["_id"],
"question": question,
"answers": [answer],
}
else:
# if the question is already in the dictionary, add the answer to the list of answers
question_answer_dict[question]["answers"].append(answer)
Expand All @@ -29,10 +35,15 @@ def restructureQnA():
# update the documents to add upvotes and downvotes to each answer
for document in combined_documents:
for answer_index, answer in enumerate(document["answers"]):
document["answers"][answer_index] = {"answer": answer, "upvotes": 0, "downvotes": 0}
document["answers"][answer_index] = {
"answer": answer,
"upvotes": 0,
"downvotes": 0,
}
pprint(combined_documents)
# insert the updated documents into the collection
collection.delete_many({})
collection.insert_many(combined_documents)

restructureQnA()

restructureQnA()
74 changes: 50 additions & 24 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@

user_specific_answers = {}


def get_chat_id(update):
"""
Returns the chatID from the update.
"""
return update.message.chat.id


def get_last_update(req, offset: int = None):
"""
Returns the latest update from the getUpdates bot API call.
Expand All @@ -47,9 +49,11 @@ def get_last_update(req, offset: int = None):
response = requests.get(updates_url, timeout=300).json()
result = response.get("result", [])
return result[-5:]
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
requests.exceptions.RequestException) as exception:
except (
requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
requests.exceptions.RequestException,
) as exception:
logger.error("Error fetching updates: %s", exception)
return []

Expand All @@ -71,7 +75,7 @@ def address_query(update):
Returns:
None.
"""

command, message = parse_incoming_message(update)

if command == "invalid":
Expand Down Expand Up @@ -123,7 +127,9 @@ def parse_incoming_message(update):

incoming_message = get_message_text(update).lower()
command, incoming_message_bifurcated = bifurcate_incoming(incoming_message)
logger.info("Parsed incoming message: command='%s', message='%s'", command, incoming_message)
logger.info(
"Parsed incoming message: command='%s', message='%s'", command, incoming_message
)
return command, incoming_message_bifurcated


Expand All @@ -146,12 +152,18 @@ def send_message(chat_id, message_text):
"""
params = {"chat_id": chat_id, "text": message_text}
try:
response = requests.post(BASE_TELEGRAM_URL + "sendMessage", timeout=10, data=params)
logger.info("Message sent successfully to chat ID %s: %s", chat_id, params["text"])
response = requests.post(
BASE_TELEGRAM_URL + "sendMessage", timeout=10, data=params
)
logger.info(
"Message sent successfully to chat ID %s: %s", chat_id, params["text"]
)
return response
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError) as exception:
except (
requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError,
) as exception:
logger.exception("Error sending message to chat ID %s: %s", chat_id, exception)


Expand All @@ -167,10 +179,13 @@ def bifurcate_incoming(incoming_message_command: str):
command, incoming_message = None, None
if incoming_message_command[0] != "/":
return "smalltalk", incoming_message_command

iterator = 1
command = ""
while iterator < len(incoming_message_command) and incoming_message_command[iterator] != " ":
while (
iterator < len(incoming_message_command)
and incoming_message_command[iterator] != " "
):
command += incoming_message_command[iterator]
iterator += 1
if iterator < len(incoming_message_command):
Expand All @@ -184,6 +199,7 @@ def bifurcate_incoming(incoming_message_command: str):
logger.exception("Error bifurcating incoming message: %s", exception)
return "invalid", None


def vote_on_answer(chat_id: int, vote_type: str, update) -> None:
"""
Registers the user's vote on a specific answer and updates the database accordingly.
Expand All @@ -200,18 +216,22 @@ def vote_on_answer(chat_id: int, vote_type: str, update) -> None:
if user_id not in user_specific_answers:
send_message(chat_id, "Your Vote could not be registered!")
return

voted_answer = user_specific_answers[user_id][-1]

user_specific_answers[user_id][-1] = voted_answer

try:
db.update_vote_qna(vote_type, answer=voted_answer["answer"])
logger.info("Vote registered successfully.")
send_message(chat_id, "Your Vote was registered!")
except Exception as exception:
logger.exception("Error registering vote: %s", exception)
send_message(chat_id, "An error occurred while registering your vote. Please try again later.")
send_message(
chat_id,
"An error occurred while registering your vote. Please try again later.",
)


def give_one_answer(chat_id: int, answers):
"""
Expand All @@ -235,10 +255,12 @@ def give_one_answer(chat_id: int, answers):
answers[i] = answers[i + 1]
answers[-1] = selected_answer

message = f"{selected_answer['answer']}\n\n" \
"___________________________________\n" \
f"Upvotes: {selected_answer['upvotes']}, Downvotes: {selected_answer['downvotes']}\n\n" \
"/u to upvote, /d to downvote, or /n for next answer."
message = (
f"{selected_answer['answer']}\n\n"
"___________________________________\n"
f"Upvotes: {selected_answer['upvotes']}, Downvotes: {selected_answer['downvotes']}\n\n"
"/u to upvote, /d to downvote, or /n for next answer."
)

send_message(chat_id, message)

Expand All @@ -248,7 +270,7 @@ def give_one_answer(chat_id: int, answers):
def answer_query(incoming_message: str, update) -> None:
"""
Answer a user's query by finding relevant answers and displaying them.

Args:
incoming_message (str): The user's query.
update (Telegram update): The Telegram update object.
Expand All @@ -259,7 +281,9 @@ def answer_query(incoming_message: str, update) -> None:
logger.info("Received answers for query: %s", incoming_message)
if len(answers) == 0:
message = "This question hasn't yet been answered. I will ask maintainers to answer it.\nTry a Google search till then:\n"
search_url = f"https://www.google.com/search?q={url.quote(incoming_message)}"
search_url = (
f"https://www.google.com/search?q={url.quote(incoming_message)}"
)
send_message(get_chat_id(update), message + search_url)
else:
answers = give_one_answer(get_chat_id(update), answers)
Expand All @@ -269,6 +293,7 @@ def answer_query(incoming_message: str, update) -> None:
error_msg = "Oops! Something went wrong while answering your query. Please try again later."
send_message(get_chat_id(update), error_msg)


def address_smalltalk(question: str) -> str:
"""
Generates a small talk response based on the given question.
Expand All @@ -286,19 +311,20 @@ def address_smalltalk(question: str) -> str:

if max(corr[0][1:]) < 0.4 or corr[0][1:].argmax() >= len(small_talk_answers):
return "I didn't understand that :("

return random.choice(small_talk_answers[corr[0][1:].argmax()].split("&&"))
except Exception as exception:
logger.exception("Error while generating small talk response: %s", exception)
return "Oops! Something went wrong while generating a response. Please try again later."


def find_answers(features):
"""
Find answers from the database for a given set of features.

Args:
features (List[float]): The features of the user's question.

Returns:
List[Dict[str, Union[str, int]]]: The list of matching answers from the database.
"""
Expand Down