Integrating API's

If you are a CS student, you may start to think about the different Focus Areas and what you may want to take.

Let's create a telebot that does the following.

  1. Shows us what Focus Areas there are in CS

  2. What modules are associated with the respective Focus Areas

  3. Retrieve information about the module

First, import the following packages

import asyncio
import nest_asyncio
from typing import Final
from telegram import (Update, InlineKeyboardButton, InlineKeyboardMarkup)
from telegram.ext import (Application, CallbackQueryHandler, CommandHandler,
                          MessageHandler, filters, ContextTypes)
from telegram.constants import ParseMode
import requests

nest_asyncio.apply()

Next, we replace the Token with our unique token ID

TOKEN: Final = '' # NEVER SHARE THIS PUBLICLY

We create an immutable list with each sublist representing a focus area

PRIMARIES_LIST: Final = [
    ['CS3230', 'CS3231', 'CS3236', 'CS4231', 'CS4234'],            # Algo
    ['CS2109S', 'CS3263', 'CS3264', 'CS4243', 'CS4246', 'CS4248'], # AI
    ['CS3241', 'CS3242', 'CS3247', 'CS4247', 'CS4350'],            # Games
    ['CS2107', 'CS3235', 'CS4236', 'CS4230', 'CS4238', 'CS4239'],  # Security
    ['CS2102', 'CS3223', 'CS4221', 'CS4224', 'CS4225'],            # Database
    ['CS2108', 'CS3245', 'CS4242', 'CS4248', 'CS4347'],            # MIR
    ['CS2105', 'CS3103', 'CS4222', 'CS4226', 'CS4231'],            # Networks
    ['CS3210', 'CS3211', 'CS4231', 'CS4223'],                      # Parallel
    ['CS2104', 'CS3211', 'CS4212', 'CS4215'],                      # Languages
    ['CS2103T', 'CS3213', 'CS3219', 'CS4211', 'CS4218', 'CS4239']  # SWE
]

We will make use of the NUSMods API: https://api.nusmods.com/v2/

If I want the query the title of a module from the module code, how would I do this?

In this instance, we set {acadYear} as 2023-2024, and the user will input their module code.

def get_mod_title(module_code: str) -> str:
    endpoint = f'https://api.nusmods.com/v2/2023-2024/'
                                 f'modules/{module_code}.json'
    response = requests.get(endpoint)
    description = response.json()
    title = description['title']
    return title

Inline Keyboard Buttons

Let's say we want to see all the focus areas via INLINE KEYBOARD AREAS

  1. Define the the function

  2. Create the Keyboard via a 2D list of of InLineKeyboardButtonobjects -> The number of InLineKeyboardButton() elements in a sublist represents the number of buttons in a row

async def focusarea(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:

    keyboard = [
        [   # 4 Buttons in a row
            InlineKeyboardButton(text="Algo & Theory", callback_data="Algorithms and Theory"),
            InlineKeyboardButton(text="AI", callback_data="Artificial Intelligence"),
            InlineKeyboardButton(text="G & G", callback_data="Computer Graphics and Games"),
        ],
        [
            InlineKeyboardButton(text="Security", callback_data="Computer Security"),
            InlineKeyboardButton(text="DB", callback_data="Database Systems"),
            InlineKeyboardButton(text="MIR", callback_data="Multimedia Information Retrieval"),
        ],
        [   # 4 buttons in a row
            InlineKeyboardButton(text="Networks", callback_data="Networking and Distributed Systems"),
            InlineKeyboardButton(text="Parallel", callback_data="Parallel Computing"),
            InlineKeyboardButton(text="Prog Langs", callback_data="Programming Languages"),
            InlineKeyboardButton(text="SWE", callback_data="Software Engineering"),
        ]
    ]

    reply_markup = InlineKeyboardMarkup(keyboard)

    await update.message.reply_text(text="Please choose a focus area: ", reply_markup=reply_markup)
  1. Inline Keyboard Markup that displays how the keyboard is displayed

  2. Sending the message "Please choose a focus area: "

    reply_markup = InlineKeyboardMarkup(keyboard)
    
    await update.message.reply_text(text="Please choose a focus area: ", reply_markup=reply_markup)

The user clicks on one of the buttons.

We want the message to change and reflect the user's chosen focus area and it's primaries. Let's create a helper function get_primaries()

  • Note: we have previously created a function get_primaries_text()

def get_primaries(focus_area_title: str) -> str:
    primaries_list = []
    match focus_area_title:
        case "Algorithms and Theory":
            primaries_list = PRIMARIES_LIST[0]
        case "Artificial Intelligence":
            primaries_list = PRIMARIES_LIST[1]
        case "Computer Graphics and Games":
            primaries_list = PRIMARIES_LIST[2]
        case "Computer Security":
            primaries_list = PRIMARIES_LIST[3]
        case "Database Systems":
            primaries_list = PRIMARIES_LIST[4]
        case "Multimedia Information Retrieval":
            primaries_list = PRIMARIES_LIST[5]
        case "Networking and Distributed Systems":
            primaries_list = PRIMARIES_LIST[6]
        case "Parallel Computing":
            primaries_list = PRIMARIES_LIST[7]
        case "Programming Languages":
            primaries_list = PRIMARIES_LIST[8]
        case "Software Engineering":
            primaries_list = PRIMARIES_LIST[9]
        case _:
            primaries_list = []

    return get_primaries_text(primaries_list)

Create the display_focus_area() function

async def display_focus_area(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    # The Inline Keyboard Provides a Callback Query that waits for the user's answer
    query = update.callback_query
    await query.answer()

    # Obtains the primaries text based on the data provided by the Query
    focus_area_primaries = get_primaries(query.data)

    # Changes the 'Please select' message to:
    await query.edit_message_text(text=f"Selected option: <b>{query.data}</b>\n"
                                       f"Enter '(MODULE_CODE)' to find out more:\n\n"
                                       f"Primaries: \n{focus_area_primaries}",
                                  parse_mode=ParseMode.HTML)

Explanation

  • query = update.callback_query

    Retrieve the callback query (info) from the update (i.e. the button the user clicked on)

  •  await query.answer()

    Informs telegram that the query has been received

  • focus_area_primaries = get_primaries(query.data)

    Obtains the text of primaries based on data from query

  • await query.edit_message_text(text=f"Selected option: <b>{query.data}</b>\n"
                                           f"Enter '(MODULE_CODE)' to find out more:\n\n"
                                           f"Primaries: \n{focus_area_primaries}",
                                      parse_mode=ParseMode.HTML)

    Changes the original message that contained the inline keyboard to -> Show the user's selected option in bold -> Prompt the user to enter a module code -> Show the primary modules in that focus area

The user inputs a module code.

def display_module(module_json) -> str:
    # Extract fields from the JSON object
    module_code = module_json.get('moduleCode', 'N/A')
    module_title = module_json.get('title', 'No Title')
    module_description = module_json.get('description', 'No Description')
    module_credits = module_json.get('moduleCredit', 'N/A')
    module_prereqs = module_json.get('prerequisite', 'None')
    module_sems = ", ".join(map(str, module_json.get('semesterData', [])))

    # Construct the message
    message = f'{module_code} {module_title}\n'
    message += f'{module_credits} MCs awarded\n'
    message += f'Semesters: {module_sems}\n'
    message += f'Prerequisites: {module_prereqs}\n'
    message += f'\nAbout: {module_description}\n'

    return message

Last updated