---
# System prepended metadata

title: 10/17 Discord Bot 開發(四)

---

# 10/17 Discord Bot 開發(四)

:::info
時間:2023/10/17 22:20~23:30
地點:線上會議(Discord)
參與:xiaojie4082

![](https://hackmd.io/_uploads/BJhqrEhZp.png)
:::

## 本次討論事項
- 講解選課相關指令
- ~~公車資訊~~ (延至下次討論)
- 機器人短時間內重複發布2000則以上訊息事件

## 講解選課相關指令
### /搜尋課程 [老師/課程] [關鍵字]
```python!
def search(course_teacher:str,keyword: str):
    try: 
        if(course_teacher == "課程"):
            # 課程名稱
            url = "https://alcat.pu.edu.tw/2011courseAbstract/main.php?" + "ls_yearsem=1121&selectno=&weekday=&section=&cus_select=&classattri=1&subjname=" + keyword + "&teaname=&opunit=&opclass=&lessonlang=&search=%E6%90%9C%E5%B0%8B&click_ok=Y"
        else:
            # 教師姓名
            url = "https://alcat.pu.edu.tw/2011courseAbstract/main.php?" + "ls_yearsem=1121&selectno=&weekday=&section=&cus_select=&classattri=1&subjname=&teaname=" + keyword + "&opunit=&opclass=&lessonlang=&search=%E6%90%9C%E5%B0%8B&click_ok=Y"
        response = requests.get(url)
        if response.status_code == 200:    
            soup = BeautifulSoup(response.text, "html.parser")
            table = soup.find('table', class_='table_info')
            rows = table.find_all('tr')
            course_data = []
            for row in rows:
                cells = row.find_all('td')
                if len(cells) >= 8: 
                    course_code = cells[0].text.strip()
                    class_name = cells[1].text.strip()
                    course_type = cells[2].text.strip()
                    course_name = cells[3].text.strip()  
                    credit = cells[5].text.strip()
                    teacher = cells[6].text.strip()
                    schedule = cells[7].text.strip()
                    course_info = {
                        '選課代號': course_code,
                        '上課班級': class_name,
                        '修別': course_type,
                        '科目名稱': course_name,
                        '學分數': credit,
                        '授課老師': teacher,
                        '上課時間地點': schedule
                    }   
                    course_data.append(course_info)
            return course_data
        else:
            course_info = {
                '選課代號': "",
                '上課班級': "",
                '修別': "",
                '科目名稱': "",
                '學分數': "",
                '授課老師': "",
                '上課時間地點': ""
            }
            return course_data
    except Exception as e:
        print(e)
        course_info = {
            '選課代號': "",
            '上課班級': "",
            '修別': "",
            '科目名稱': "",
            '學分數': "",
            '授課老師': "",
            '上課時間地點': ""
        }
        return course_data
```

### /課程綱要 [課程代號]
```python!
def syllabus(selectno: str):
    try: 
        url = 'https://alcat.pu.edu.tw/2011courseAbstract/main.php?ls_yearsem=1121&selectno=' + selectno + '&weekday=&section=&cus_select=&classattri=1&subjname=&teaname=&opunit=&opclass=&lessonlang=&search=%E6%90%9C%E5%B0%8B&click_ok=Y'
        response = requests.get(url)
        if response.status_code == 200:    
            soup = BeautifulSoup(response.text, "html.parser")     
            all_a_elements = soup.find_all('a', href=True)
            for a_element in all_a_elements:
                href = a_element['href'] 
                if "list_abstract_content_100.php?pkey=" in href:
                    new_href = href.replace("..", "https://alcat.pu.edu.tw/")
                    response = requests.get(new_href)
                    soup = BeautifulSoup(response.text, 'html.parser')
                    # 課程名稱
                    course_name_element = soup.find(text="課程名稱")
                    if course_name_element:
                        course_name = course_name_element.find_next('td').get_text(strip=True)
                    else:
                        course_name = "未找到課程名稱"
                    # 授課教師
                    instructor_element = soup.find(text="授課教師")
                    if instructor_element:
                        instructor = instructor_element.find_next('td').get_text(strip=True)
                    else:
                        instructor = "未找到授課教師"
                    # 上課時段
                    class_time_element = soup.find(text="上課時段")
                    if class_time_element:
                        class_time = class_time_element.find_next('td').get_text(strip=True)
                    else:
                        class_time = "未找到上課時段"
                    # 中文版課程簡介
                    course_description = soup.find('th', string='中文版課程簡介Course Description（Chinese Version）').find_next('td').text.strip()
                    if len(course_description) > 500:
                        course_description = course_description[:500] + "\n\n...略"
                    # 評分方式及比重
                    grading_content = soup.find('th', string="評分方式及比重Grading Methods and Ratio ").find_next('td').text.strip()
                    # 提取各科授課進度與內容Course Syllabus and Teaching Content
                    syllabus_and_content = soup.find('th', string='各科授課進度與內容Course Syllabus and Teaching Content').find_next('td').text.strip()   
                    syllabus_and_content = syllabus_and_content.replace("週次", "").replace("Week（Time）", "").replace("主題內容/章節/或活動", "").replace("Topic/Chapter/Classroom Activities", "").replace("備註", "").replace("Remark(s)", "")
                    lines = syllabus_and_content.strip().split('\n\n')
                    weekly_contents = [line.strip() for line in lines if line.strip()]
                    syllabus_and_content = ""  
                    for week, content in enumerate(weekly_contents, start=1):
                        syllabus_and_content = syllabus_and_content + content + "\n\n"
                    # PUHub 課程評論
                    url = "https://puhub.org/api/course_evaluation.php?course=" + course_name + "&teacher=" + instructor
                    response = requests.get(url)
                    datas = response.json()
                    evaluation = ""
                    temp = 0
                    for data in datas:
                        temp = temp + 1
                        evaluation = evaluation + "```" + data[6] + "``` "
                    if temp == 0:
                        evaluation = "```目前沒有任何評論```"
                    # AI 分析
                    # info = "## 課程名稱：" + course_name + "\t授課教師：" + instructor + "\t上課時段：" + class_time + "\n### 課程簡介：\n```" + course_description + "```\n### 評分方式：\n```" + grading_content + "```"
                    # messages = [{'role': 'user','content': "我是個大學生，正在尋找適合自己的課程，請透過下面的資訊向我提供選課分析。(200字以內)\n" + info + syllabus_and_content}]
                    # gpt_35_api_stream(messages)
                    # content = messages[1]['content']
                    content = ""
                    return course_name, instructor, class_time, course_description, grading_content, evaluation, content, new_href, url
    except Exception as e:
        print(e)
        return "", "", "", "", "", "", "", "", ""
```

### /課程餘額 [課程代號]
```python!
def person(selectno: int):
    try: 
        url = 'https://alcat.pu.edu.tw/choice/q_person.html'
        data = {
            'selectno': selectno
        }
        response = requests.post(url, data=data)
        if response.status_code == 200:
            soup = BeautifulSoup(response.text, "html.parser")
            course_name = soup.find('h2', string=lambda text: text and '科目名稱：' in text).text
            course_name = course_name.replace('科目名稱：', '')
            rows = soup.find_all('tr')
            limit = None
            enrollment = None
            remaining = None
            for row in rows:
                cells = row.find_all('td')
                if len(cells) == 2:
                    key = cells[0].text.strip()
                    value = cells[1].text.strip()
                    if key == '人數上限':
                        limit = value
                    elif key == '修課人數':
                        enrollment = value
                    # elif key == '人數餘額':
                    #     remaining = value
            remaining = int(limit) - int(enrollment)
            return course_name,enrollment,remaining
        else:
            return "輸入的資料有誤","輸入的資料有誤","輸入的資料有誤"
    except Exception as e:
        print(e)
        return "輸入的資料有誤","輸入的資料有誤","輸入的資料有誤"
```

## 機器人短時間內重複發布2000則以上訊息事件

- 事件名稱：Discord Bot 持續發送訊息
- 事件時間：2023-10-16 早上 6. ~ 7.30
- 事件緣起：因 API 資料錯誤(缺漏)，導致機器人持續發送訊息(陷入無限迴圈直至API資料恢復)，造成所有成員困擾。
- 事件經過：
    1.  2023-10-16 上午 6 點，成員反應 Discord Bot 持續發送訊息。
    2.  管理團隊調查後發現，是 Bot 的 API 資料錯誤，導致 Bot 陷入無限迴圈，不斷發送訊息。
    3.  管理團隊已將頻道暫時關閉，並對 Bot 添加 API 資料偵測，以防止此類事件再次發生。
    4.  目前，已對機器人添加例外處理，避免發生相同狀況陷入無限迴圈。
- 事件分析：此次事件的原因是 中央氣象署 的 API 資料錯誤 以及 程式碼缺陷。造成此錯誤的原因可能是： 
    - 程式碼有 bug。
    - API 資料有問題。
- 事件後續：
    - 服務頻道改為選擇制
    - 程式碼添加例外處理

## 下次會議討論事項
- 10/21 Discord Bot 開發(五)
    - 公車資訊
    - 添加按鈕
