返回开发者新闻

Sending Messages with WhatsApp in Your Python Applications

2022年10月24日发布者:Dmitry Vinnik

WhatsApp Business 开放平台提供的工具可自动发送、接收消息以及处理传入消息,让商家能够与其受众更密切地交流。例如,如果使用自动发消息功能,系统可在新客户于非营业时间与您联系时欢迎他们,或向他们发送通知。

本文介绍如何集成 Meta 托管的云端 API,从而实现 WhatsApp Business 开放平台与 Python 应用程序的集成,以支持发送和管理 WhatsApp 消息。

我们来深入探索一下如何从头开始创建支持 WhatsApp 消息功能的 Python 网页应用。如果您想预览最终成果,可以下载完整的应用程序代码

要求

若要使用测试电话号码发送和接收消息,请按设置开发者资产和开放平台访问权限教程操作,确保完成以下步骤:

Meta 开发者中注册一个免费的开发者帐户。

为帐户启用双重验证:

创建 Meta 应用。本文稍后将使用应用编号和应用密钥。

关联 Meta 应用与 WhatsApp 产品。

关联应用与商务管理平台。

在应用面板上,打开“WhatsApp”>“开始”菜单,然后配置收信人电话号码。您的应用需要使用该号码作为 WhatsApp 消息的收信人。本文稍后将使用此号码。

为业务帐户创建系统用户

在“系统用户”页面上,为新系统用户生成新口令,分配 WhatsApp 应用和所有可用权限。本文稍后将使用此口令。

在“系统用户”页面上,为系统用户配置资产,分配 WhatsApp 应用的完全控制权。请记得点击“保存更改”按钮。

最后要强调的是,如果尚未下载和安装 Python,请执行此操作。

要构建的应用

我们的小应用程序示例将用作线上机票预订服务应用。此应用程序将使用 API 为用户提供比邮件交流更个性化的互动体验。当用户登录时,此应用程序通过 WhatsApp 消息向他们发出问候。然后,在用户购买机票后,他们将收到确认购买机票的消息。

使用 Python 和 Flask 创建极简应用

本版块将帮助您正常运行新的 Python 项目。我们将使用 Jinja(轻型模板引擎)和 Flask(微网页框架)。

首先,打开一个终端,并为您的项目创建一个文件夹。然后执行以下命令:

python3 -m venv venv 

此命令将为 Python 项目创建虚拟布景。

然后,执行以下命令:

$ mkdir myproject
$ cd myproject
$ python3 -m venv venv

随后激活虚拟布景。

$ . venv/bin/activate

现在,安装 Flask:

pip install flask[async]

使用以下内容在项目根目录下创建 app.py 文件:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

运行应用:

$ flask run

然后,您将看到应用在本地端口 5000 运行:

 * Serving Flask app 'app.py' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)

现在,访问 http://127.0.0.1:5000/,您将看到采用 Python 和 Flask 的入门级应用程序的首页:

创建登录页面示例

若要开始使用机票应用程序,可创建一个登录表单示例作为首页。您需要调用 render_template 函数来渲染单独的 HTML 文件中的视图。打开 app.py 文件进行修改,以导入 render_template 函数:

from flask import Flask, render_template

然后将 hello_world 替换为索引函数,如下所示:

def index():
    return render_template('index.html', name=__name__)

新建名为“templates”的文件夹,然后新建名为“index.html”的文件:

\templates
	|--- index.html

随后,打开 index.html 文件,并添加以下 HTML 内容。在此创建的登录示例附带登录占位符和密码。这样,无需提供这些内容即可使用此应用程序。

对于网页应用前端,我们使用 Bootstrap。此资源库很受欢迎,可帮助您构建一致的响应式轻型用户界面,以便在不同设备上轻松运行应用,而不必考虑 CSS 规则:

<!DOCTYPE html>
<html>
  <head>
    <title>Flight Confirmation Demo for Python</title>
    <h1 class="text-center">Flight Confirmation Demo for Python</h1>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <div class="d-flex flex-row justify-content-center align-items-center">
      <div class="border px-3">
        <div class="row">
          <div class="col-sm-6 d-none d-sm-block">
            <img
              src="https://images.unsplash.com/photo-1530521954074-e64f6810b32d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NHx8YWlyJTIwdHJhdmVsfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60"
              alt="Login image"
              class="w-100 vh-50 pt-3 pb-3"
              style="object-fit: cover; object-position: left"
            />
          </div>
          <div class="col-sm-6 text-black">
            <div class="px-5 ms-xl-4">
              <i
                class="fas fa-crow fa-2x me-3 pt-5 mt-xl-4"
                style="color: #709085"
              ></i>
            </div>

            <div class="d-flex align-items-center h-custom-2">
              <form class="w-100" method="post" action="/welcome">
                <div class="form-outline mb-4">
                  <input
                    type="text"
                    value="this_is_a_demo@email.com"
                    disabled
                    class="form-control form-control-md text-muted"
                  />
                </div>

                <div class="form-outline mb-4">
                  <input
                    type="text"
                    value="••••••••••••••••"
                    disabled
                    id="form2Example28"
                    class="form-control form-control-md text-muted"
                  />
                </div>

                <div class="pt-1 mb-4">
                  <input type="submit" class="btn btn-info btn-lg btn-block" value="Login"/>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
     
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  </body>
</html>

然后,再次运行应用,您将看到新登录页面:

> flask run

使用 Python 和 WhatsApp Business 发送短信

Python 应用程序需要使用本文开始部分创建的 Meta 开发者帐户中的特定数据。在开发期间,为便于将所有配置保存在一个位置,而不是将其分散在代码之间,请将其置于一个文件中。

使用以下设置,在项目根目录下创建 config.json 文件,并将所有占位符替换为 WhatsApp Business 商业帐号面板中的详细信息:

{
  "APP_ID": "<<YOUR-WHATSAPP-BUSINESS-APP_ID>>",
  "APP_SECRET": "<<YOUR-WHATSAPP-BUSINESS-APP_SECRET>>",
  "RECIPIENT_WAID": "<<YOUR-RECIPIENT-TEST-PHONE-NUMBER>>",
  "VERSION": "v13.0",
  "PHONE_NUMBER_ID": "<<YOUR-WHATSAPP-BUSINESS-PHONE-NUMBER-ID>>",
  "ACCESS_TOKEN": "<<YOUR-SYSTEM-USER-ACCESS-TOKEN>>"
}

执行登录表单操作后,应用将向 /welcome 线路发出 POST 请求。因此,您将需要使用新路由器执行以下操作:

  • 处理“欢迎”HTTP POST 请求。
  • 获取欢迎消息所需的配置。
  • 通过 API 发送欢迎消息。
  • 发送此消息后,立即将应用重定向至首页。

现在,打开 app.py 文件,并将其内容替换为以下代码,以加入 /welcome 端点:

import json
from flask import Flask, render_template
import flask
from message_helper import get_text_message_input, send_message

app = Flask(__name__)

with open('config.json') as f:
    config = json.load(f)

app.config.update(config)

@app.route("/")
def index():
    return render_template('index.html', name=__name__)

@app.route('/welcome', methods=['POST'])
async def welcome():
  data = get_text_message_input(app.config['RECIPIENT_WAID']
                                , 'Welcome to the Flight Confirmation Demo App for Python!');
  await send_message(data)
  return flask.redirect(flask.url_for('index'))

现在,安装 aiohttp 以使应用执行异步 HTTP 请求:

pip install aiohttp[speedups]

随后,您需要使用函数封装通过 API 发送文本型消息的代码。使用以下代码,在项目根目录下新建 message_helper.py 文件:

import aiohttp
import json
from flask import current_app

async def send_message(data):
  headers = {
    "Content-type": "application/json",
    "Authorization": f"Bearer {current_app.config['ACCESS_TOKEN']}",
    }
  
  async with aiohttp.ClientSession() as session:
    url = 'https://graph.facebook.com' + f"/{current_app.config['VERSION']}/{current_app.config['PHONE_NUMBER_ID']}/messages"
    try:
      async with session.post(url, data=data, headers=headers) as response:
        if response.status == 200:
          print("Status:", response.status)
          print("Content-type:", response.headers['content-type'])

          html = await response.text()
          print("Body:", html)
        else:
          print(response.status)        
          print(response)        
    except aiohttp.ClientConnectorError as e:
      print('Connection Error', str(e))

def get_text_message_input(recipient, text):
  return json.dumps({
    "messaging_product": "whatsapp",
    "preview_url": False,
    "recipient_type": "individual",
    "to": recipient,
    "type": "text",
    "text": {
        "body": text
    }
  })

使用上述代码,可通过 graph.facebook.com 的 Meta 图谱 API 向 /messages 端点发出 HTTP POST 请求,传递以下内容:

  • 您使用的云端 API 版本
  • 将接收消息的测试电话号码(已配置此号码)
  • 为系统用户生成的访问口令

另请注意,get_text_message_input 函数将返回发送基本短信所需的特定数据结构。

最后,再次运行应用:

> flask run

然后点击登录按钮。您将看到屏幕上弹出 WhatsApp 通知:

点击该通知以打开 WhatsApp 应用,您将看到 Python 应用程序所发送的基本短信:

目前,您已可以使用 WhatsApp 发送简单消息。之后,您将能够使用模板发送更复杂的消息。

创建航班目录页面

首先,您将创建可售票航班的目录及其详细信息,以便线上客户购票。此数据将存储在单独的文件中。使用以下内容,新建 \flights.py 文件:

def get_flights():
  return [{
    "flight_id": 1,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Aerial_view_of_Bajra_Sandhi_Monument_Denpasar_Bali_Indonesia.jpg/250px-Aerial_view_of_Bajra_Sandhi_Monument_Denpasar_Bali_Indonesia.jpg',
    "origin": 'Singapore (SIN)',
    "destination": 'Denpasar (DPS)',
    "time": 'June 24, 2022 - 8:25 PM'
  },
  {
    "flight_id": 2,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/7/75/Parliament_at_Sunset.JPG/275px-Parliament_at_Sunset.JPG',
    "origin": 'New York (JFK)',
    "destination": 'London (LHR)',
    "time": 'June 25, 2022 - 9:15 PM'
  },
  {
    "flight_id": 3,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Sydney_Australia._%2821339175489%29.jpg/250px-Sydney_Australia._%2821339175489%29.jpg',
    "origin": 'Beijing (PEK)',
    "destination": 'Sydney (SYD)',
    "time": 'June 25, 2022 - 5:30 AM'
  },
  {
    "flight_id": 4,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Mouth_of_Miami_River_20100211.jpg/220px-Mouth_of_Miami_River_20100211.jpg',
    "origin": 'São Paulo (GRU)',
    "destination": 'Miami (MIA)',
    "time": 'June 25, 2022 - 9:25 AM'
  }]

现在需要为用户设置新线路来访问航班目录页面。打开 app.py 文件,并导入 get_flight 函数:

from flights import get_flights

然后,在 app.py 文件中添加目录函数:

@app.route("/catalog")
def catalog():
    return render_template('catalog.html', title='Flight Confirmation Demo for Python', flights=get_flights())

修改欢迎函数,以重定向至目录页面,而非索引页面:

 return flask.redirect(flask.url_for('catalog'))

最后,使用以下内容,在 templates\catalog.html 中新建文件:

<!DOCTYPE html>
<html>
  <head>
    <title>{{title}}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
  </head>
  <body>
    <h1 class="text-center">{{title}}</h1>
    <p class="text-center">Welcome to {{title}}</p>

    <div class="container"> 
      <div class="row">
        {% for flight in flights %}
          <div class="col col-3">
            <div class="card">
              <img src="{{ flight.thumbnail }}" class="card-img-top" alt="{{flight.origin}}"/>
              <div class="card-body">
                <p class="card-text"><b>origin:</b> {{ flight.origin }}</p>
                <p class="card-text"><b>destination:</b> {{ flight.destination }}</p>
                <p class="card-text"><b>time:</b> {{ flight.time }}</p>
                <p>
                  <form method="POST" action="/buy-ticket">
                    <div class="container">
                      <input type="hidden" name="id" value="{{flight.flight_id}}"/>
                      <div class="row gx-5">
                        <input type="submit" value="&#127915; Buy" class="col col-5"/>
                      </div>
                    </div>
                  </form>
                </p>
              </div>
            </div>
          </div>
        {% endfor %}
      </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
</html>

再次运行应用,然后点击登录按钮。这样,系统将向您的 WhatsApp 号码发送欢迎消息。此外,还会将您重定向至 /catalog 视图:

> flask run

请注意,上方屏幕上显示的每个航班都有一个购票按钮。随后,您将配置应用程序以处理购票事务。

使用 Python 和 WhatsApp Business 发送模板消息

必须使用消息模板才能开启商家发起的对话。这些对话可以是客户服务消息,也可以是预约提醒、支付或配送更新、提醒等。

打开 app.py 文件,并添加导入的 get_templated_message_input 和请求函数:

from flask import Flask, render_template, request
from message_helper import get_templated_message_input, get_text_message_input, send_message

然后,使用以下内容为 /buy-ticket 线路添加新函数:

@app.route("/buy-ticket", methods=['POST'])
async def buy_ticket():
  flight_id = int(request.form.get("id"))
  flights = get_flights()
  flight = next(filter(lambda f: f['flight_id'] == flight_id, flights), None)
  data = get_templated_message_input(app.config['RECIPIENT_WAID'], flight)
  await send_message(data)
  return flask.redirect(flask.url_for('catalog'))

随后,打开 message_helper.py 文件,并加入 get_templated_message_input 函数:

def get_templated_message_input(recipient, flight):
  return json.dumps({
    "messaging_product": "whatsapp",
    "to": recipient,
    "type": "template",
    "template": {
      "name": "sample_flight_confirmation",
      "language": {
        "code": "en_US"
      },
      "components": [
        {
          "type": "header",
          "parameters": [
            {
              "type": "document",
              "document": {
                "filename": "FlightConfirmation.pdf",
                "link": flight['document']
              }
            }
          ]
        },
        {
          "type": "body",
          "parameters": [
            {
              "type": "text",
              "text": flight['origin']
            },
            {
              "type": "text",
              "text": flight['destination']
            },
            {
              "type": "text",
              "text": flight['time']
            }
          ]
        }
      ]
    }
  })

请注意,我们在上面的代码中使用了 sample_flight_confirmation 模板,其中提供了航班文档 PDF 文件、航班始发地、目的地和日期/时间。您可以使用其他可用模板实验,也可以通过访问消息模板页面新建模板。

最后,再次运行应用,然后点击其中一个“购买”按钮。这样,应用将通过 WhatsApp 向测试电话号码发送模板消息:

> flask run

现在,打开 WhatsApp 应用,您将看到此模板消息。

就是这么简单!

如您所见,使用 Python 代码发送消息很简单。但请注意以下集成 WhatsApp 与应用程序的技巧和最佳实践:

  • 即使是自动发送应用消息,也需确保与客户的交流自然流畅。用户希望享受更个性化的体验,请确保发送更个性化的消息。
  • 探索更轻松随意的语气。但是,避免出现句法或语法错误。
  • 确保文本简明扼要。
  • 使用模板时,通过使用文档、视频或图像链接,提供丰富的上下文信息,就像上文描述与机票有关的航班信息所使用的内容一样。

结论

本文介绍了如何通过集成 Python 应用与 WhatsApp Business 商业帐号,在此 Python 应用中添加发消息功能。

从头开始创建简单的 Python 应用程序后,添加登录页面示例,并配置应用程序以通过云端 API 向用户发送基本欢迎消息。最后,添加目录页面,并配置该页面,以发送提供航班确认详细信息的模板消息。

本文介绍的配置内容只是冰山一角。想要了解如何在应用程序中配置 Webhooks,配置有关发送和接收客户消息及业务帐户信息的通知吗?请查看 WhatsApp Business 开放平台文档,了解此内容以及更多信息。