PecuLab 教育元宇宙
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 程式碼實作 **2 hr** [ToC] ## 1. Django 設定 1. 安裝套件 ``` $ pip install Django ``` ``` $ pip install line-bot-sdk ``` ``` $ pip install pymysql ``` 2. 建立專案 ``` $ django-admin startproject '專案名稱' ``` ``` cd '專案名稱' ``` 3. 建立 APP:可以進行多個 Line Bot 開發 ``` $ python manage.py startapp 'APP名稱' ``` 4. 新建兩個資料夾 ``` $ md static $ md templates ``` static 用於靜態資料如圖片、檔案 templates 用於放寫好的 html ## 2. 在 `settings.py` 進行 1. 在最上面 ```python import pymysql pymysql.install_as_MySQLdb() ``` 2. 設定 Channel Access Token & Channel Secret 第 22 行左右的地方 ```python= CHANNEL_ACCESS_TOKEN = 'Channel_access_token' CHANNEL_SECRET = 'Channel_secret' ``` 3. 於 INSTALLED_APPS 中設定建立的 APP 名稱 ```python= INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', '【app】' #新增 app 名稱 ] ``` 4. 於 TEMPLATES 中設定 templates 的資料夾路徑 ```python 'DIRS'[os.path.join(BASE_DIR,'templates')] ``` 5. staitc ```python STATIC_URL = '/static' STATICILES_DIRS = [os.path.join(BASE_DIR,'static')] ``` 6. 語言及時區設定 ## 利用 Django 連接資料庫 (共 7 個步驟) 1. `settings.py` ```python= DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': '【your DB name】', 'USER': '【your user name】', 'PASSWORD': '【your password】', 'HOST': '', 'PORT': '3306', } } ``` 2. debug > 如果MySQL 服務器配置為使用 caching_sha2_password 或 sha256_password 作為身份驗證插件時,PyMySQL(這是 Django 用來與 MySQL 通信的一個 Python 庫)需要 cryptography 套件來加密密碼。 ``` $ pip install cryptography ``` 3. 同步資料庫 ``` $ python manage.py inspectdb > 【your_app_name】/models.py ``` 4. 檢查 `model.py` 有沒有你的 database 5. `admin.py` (後台資料) 5-1 from `models.py` import the database table ```python= from django.contrib import admin # Register your models here. from 【your_app_name】.models import * #【below is optional,想要做 5-2 再做就可以】 class user_message_admin(admin.ModelAdmin): list_display = ('uid', 'name', 'message', 'time') admin.site.register(user_message, user_message_admin) ``` 5-2 optional 若我們想從 Linebot 中記錄用戶 ID 以及訊息內容 ```python= #from django.db import models # Create your models here. class user_message(models.Model): uid = models.CharField(max_length=50, null=False, primary_key=True) # user id name = models.CharField(max_length=50, blank=True, null=False) # LINE 名字 message = models.CharField(max_length=600, blank=True, null=False) # 文字訊息紀錄 time = models.DateTimeField(auto_now=True) # 日期時間 def __str__(self): return self.name # print the user name in terminal ``` 7. 更新資料庫 之後在 mysql workbench 有異動都可以直接輸這個更新 ``` $ python manage.py makemigrations ``` ``` $ python manage.py migrate ``` 7-1. debug 有可能出現 >SyntaxError: source code string cannot contain null bytes - 意思是有 null bytes 出現,而非一般的空格(ex. a + b,就屬於一般空格) - 解決方式: (1) 下載 hex editor (發行者 :microsoft) (2) 右鍵點選檔案>開啟方式> 16 進位編輯器 > crtl+F 查找`00` $\quad$在 16 進位中,一般空格應顯示`20` $\quad$範例圖示如下 STEP1 ![image](https://hackmd.io/_uploads/S16fIhqA6.png) STEP2 ![image](https://hackmd.io/_uploads/BkZr8nc0T.png) STEP 3 注意要選擇「以二進位模式搜尋」(會自動變成 16 進位不用擔心) ![image](https://hackmd.io/_uploads/BkTKI290a.png) (3) TIP $\quad$因為 `model.py` 的 table 都是從 mysql 導入,比較有可能出錯,所以可以先從這邊下手 (4) $\quad$ 4-1 找到有錯的檔案之後,用文字編輯器打開,複製所有程式。 $\quad$$\quad$$\quad$重新建立一個`.py`檔案,貼上 `models.py` 的內容。 $\quad$ 4-2 貼上後一樣用 16 進位的方式確認沒有出現 null bytes (`00`) $\quad$ 4-3 用新檔案取代舊的 `models.py` (一樣的路徑、一樣的名字) (5) 重新輸入 `$ python manage.py makemigrations`,確認沒有問題 - 這樣就 debug 結束了~ ## 資料表操作 1. `views.py` 2. 將` models.py` 資料表匯入 `views.py` ```python from 【your_app_name】.models import * ``` 3. 修改 `views.py` ```python= from django.shortcuts import render # Create your views here. from app.models import * import re import logging from django.conf import settings from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden from django.views.decorators.csrf import csrf_exempt from linebot import LineBotApi, WebhookParser from linebot.exceptions import InvalidSignatureError, LineBotApiError from linebot.models import MessageEvent, TextSendMessage from .models import SnakeSpecies, Product, CareRequirements line_bot_api = LineBotApi(settings.CHANNEL_ACCESS_TOKEN) parser = WebhookParser(settings.CHANNEL_SECRET) # 設置日誌 logger = logging.getLogger(__name__) @csrf_exempt def callback(request): if request.method != 'POST': return HttpResponseBadRequest() signature = request.META.get('HTTP_X_LINE_SIGNATURE', '') body = request.body.decode('utf-8') # 日誌輸出 logger.info(f"Request body: {body}") logger.info(f"Signature: {signature}") try: events = parser.parse(body, signature) except InvalidSignatureError: logger.error("Invalid signature error") return HttpResponseForbidden() except LineBotApiError as e: logger.error(f"LineBotApiError: {str(e)}") return HttpResponseBadRequest() for event in events: if isinstance(event, MessageEvent): handle_message(event) return HttpResponse('OK') def handle_message(event): message_text = event.message.text.strip() if "蛇的學名" in message_text: scientific_name = extract_scientific_name(message_text) species_exists = SnakeSpecies.objects.filter(scientific_name=scientific_name).exists() if species_exists: reply_text = f"蛇類 {scientific_name} 已經存在於資料庫中。" else: reply_text = f"您所輸入的蛇類不存在,請您建立新的檔案。若要建立,請回覆 OK" line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) elif "new" in message_text: # 调用 handle_snake_species 并传递 message_text,这个函数负责发送回复 handle_snake_species(event, message_text) elif "【學名】" in message_text: handle_read(event, message_text) elif "刪除產品編號" in message_text: handle_delete(event, message_text) elif "更新" in message_text: handle_update(event, message_text) def extract_scientific_name(text): # 假设用户会按照【學名】snake的格式输入 pattern = r'蛇的學名[::](.+)' match = re.search(pattern, text) return match.group(1) if match else None def extract_scientific_name2(text): # 假设用户会按照【學名】snake的格式输入 pattern = r'【學名】(\w+\s?\w*)' match = re.search(pattern, text) return match.group(1) if match else None def handle_snake_species(event, message_text): data = parse_data(message_text) if data: species, created = SnakeSpecies.objects.get_or_create( scientific_name=data['scientific_name'], chinese_name=data['chinese_name'], defaults={'amount': data['amount']} ) if created: #reply_text = f"儲存成功,蛇類編號為 {species.id}" //這個有錯,好像讀不出來整個掰掰 reply_text = "儲存成功" else: reply_text = "儲存失敗" else: reply_text = "儲存失敗,請確認訊息格式是否有誤" line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) def parse_data(text): pattern = r'.*\n【學名】(\w+)\n【中文名稱】(\S+)\n【數量】(\d+)' match = re.search(pattern, text, re.MULTILINE | re.DOTALL) if match: return { 'scientific_name': match.group(1), 'chinese_name': match.group(2), 'amount': int(match.group(3)) } return None def handle_read(event, message_text): # 提取学名 scientific_name = extract_scientific_name2(message_text) if not scientific_name: line_bot_api.reply_message(event.reply_token, TextSendMessage(text="格式错误,请重新输入。")) return # 查询数据库,获取所有匹配的 SnakeSpecies 对象 matching_species = SnakeSpecies.objects.filter(scientific_name=scientific_name) if matching_species: # 构建产品名称列表 product_names = [f"{species.chinese_name} (數量: {species.amount})" for species in matching_species] product_names_str = "\n".join(product_names) reply_text = f"以下是與學名 '{scientific_name}' 匹配的產品:\n\n{product_names_str}" line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) return # 如果没有匹配的对象 line_bot_api.reply_message(event.reply_token, TextSendMessage(text="没有找到对应的蛇类。")) return def handle_update(event, message_text): pattern = r'【產品編號:(\d+)】(\d+)' match = re.search(pattern, message_text) if match: product_id, new_price = match.groups() try: # 尝试获取产品 product = Product.objects.get(product_id=int(product_id)) # 更新价格 product.price = float(new_price) product.save() reply_text = f"產品編號 {product_id} 的價格已更新為 {new_price}" except Product.DoesNotExist: reply_text = f"没有找到產品編號 {product_id} 的產品。" except Exception as e: reply_text = f"錯誤: {str(e)}" else: reply_text = "格式錯誤" # 发送回复消息 line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) def handle_delete(event, message_text): # 假设用户输入格式为:“【刪除產品編號】12345” pattern = r'【刪除產品編號】(\d+)' match = re.search(pattern, message_text) if not match: line_bot_api.reply_message(event.reply_token, TextSendMessage(text="格式錯誤")) return product_id = int(match.group(1)) try: product = Product.objects.get(product_id=product_id) product.delete() reply_text = f"產品編號 {product_id} 已成功删除。" except Product.DoesNotExist: reply_text = f"沒有找到 {product_id} " except Exception as e: reply_text = f"刪除過程中出現錯誤 {str(e)}" line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully