【问题标题】:Broadcasting to Members of a Database Using Twilio Messaging Services使用 Twilio 消息服务向数据库成员广播
【发布时间】:2021-09-10 19:35:43
【问题描述】:

Stack Overflow 的好人。

我正在尝试解决我遇到的涉及 twilio 函数、消息传递服务和数据库的问题。

我正在尝试做的是一次向数据库的所有成员发送消息。

我的代码一团糟,因为 Javascript 不是我的母语,而且我对 twilio 还很陌生。

我认为我遇到的问题是 javascript 的 async/await 功能。

到目前为止,这是我的代码:

// Boiler Plate Deta Code
const { Deta } = require("deta");

// Function to access database and get object of conta
async function FetchDB(db) {
    let res = await db.fetch();
    allItems = res.items;
    
    // continue fetching until last is not seen
    while (res.last){
      res = await db.fetch({}, {last: res.last});
      allItems = allItems.concat(res.items);
    }
}

// Function to get total number of contacts.
async function ReturnNumberOfContacts(allItems) {
  number_of_contacts = allItems.length;
}

// Function to send message to contact in database.
async function SendMessages(allItems, message) {
       allItems.forEach(contact => {
       let users_name = contact.name
         client.messages
          .create({
            body: `Hey ${users_name}! ${message}`,
            messagingServiceSid: messaging_service,
            to: contact.key
         })
    });
}

// Function to submit response to broadcaster.
async function SuccessResponse(user_name, number_of_contacts) {
   responseObject = {
    "actions": [
      {
        "say": `${user_name}, your broadcast has successfully sent to ${number_of_contacts} contacts.`
      },
      {
        "listen": true
      }
    ]
  }

}

// Main Function
exports.handler = async function(context, event, callback) {
  
  // Placeholder for number of contacts
  let number_of_contacts;
  
  // Place holder for object from database of all contacts
  let allItems;
  
  // Placeholder for users message
  let message;
  
  // Placeholder for response to user
    let responseObject;
  
  //Twilio and Deta, Etc Const
  const client = require('twilio')(context.ACCOUNT_SID, context.AUTH_TOKEN);
  const deta = Deta(context.DETA_PROJECT_KEY);
  const db = deta.Base("users2");
  const messaging_service = context.MESSAGING_SERVICE;
  
  // From Phone Number
  const from = event.UserIdentifier;
  
  // Parse memory
  const memory = JSON.parse(event.Memory);

  // Fetch all items from database and return total number of contacts.
  // Update relavent variables
  await FetchDB(db, allItems).then(ReturnNumberOfContacts(allItems));
  
  // Figure out if message came from short circuit broadcast or normal
  if (memory.triggered) {
    message = memory.message;
  } else {
    message = memory.twilio.collected_data.broadcast_message.answers.message_input.answer;
  }
  
  // Check if verified and set name.
  const current_user = await db.get(from);
  
  // Get the current users name or set a default value
  let user_name = current_user.name || "friend";

  // Determine if user is an authorized broadcaster
  if (from === context.BROADCAST_NUMBER) {
  
  // Decide if the sending of a message should be cancelled.
  if (message.toLowerCase() === "c" || message.toLowerCase() === "cancel") {
    responseObject = {
      "actions": [
        {
          "say": `${user_name}, you have canceled your request and no messages have been sent.`
       },
       {
          "listen": false
       }
      ]
   }
  // Return Callback and end task
  callback(null, responseObject);
  }
  // Move forward with sending a message.
  else {
      // Send message to users in database and send success message to broadcaster.
      await SendMessages(message, client, messaging_service)
      .then(SuccessResponse(user_name, number_of_contacts))
      return callback(null, responseObject);
  }

// The user is not authorized so return this.
}
  return callback(null, {
    "actions": [
      {
        "say": "You are not authorized to broadcast."
      },
      {
        "listen": false
      }
    ]
  })
};

因此,当触发 Fetch() 函数时,我希望数据库加载每个人的列表,并让 twilio 向他们发送保存在 message 变量中的所需消息。我有代码工作,以便我可以从数据库中读取并获取正确的值,并发送一条带有所需消息的文本消息,但我现在遇到的问题是将它们整合在一起。

如果有人能在这里指出正确的方向,谢谢。

再次,我是 javascript 的新手,更具体地说是异步编程。

【问题讨论】:

  • 这是我现在遇到的错误 >>> 函数执行导致错误日志:UnhandledPromiseRejectionWarning:未处理的承诺拒绝:TypeError:无法读取 ReturnNumberOfContacts (/var) 处未定义的属性“长度” /task/handlers/ZN166fcaef71e8b34f546263b2198e8d04.js:18:33) 在 Object.exports.handler (/var/task/handlers/ZN166fcaef71e8b34f546263b2198e8d04.js:78:36) 在 Object.exports.runtime (/var/task/node_modules/ -handler/index.js:310:10) 在 Runtime.exports.handler (/var/task/runtime-handler.js:17:17) 在 Runtime.handleOnce (/var/run...
  • 另外,我正在 twilio 函数中构建它。

标签: twilio


【解决方案1】:

这里是 Twilio 开发者宣传员。

当您调用ReturnNumberOfContacts 时,错误的问题是allItemsundefined

我认为问题出在尝试使用allItems 作为一种全局变量,number_of_contacts 也是如此。 FetchDB 用项目列表来解决,ReturnNumberOfContacts 用项目数量来解决会更好。

当您在函数中调用 SendMessages 时,您还缺少一些参数。我已经将它更新到我认为它会起作用的程度:

// Boiler Plate Deta Code
const { Deta } = require("deta");

// Function to access database and get object of conta
async function FetchDB(db) {
  let res = await db.fetch();
  let allItems = res.items;

  // continue fetching until last is not seen
  while (res.last) {
    res = await db.fetch({}, { last: res.last });
    allItems = allItems.concat(res.items);
  }
  return allItems;
}

// Function to send message to contact in database.
async function SendMessages(allItems, message, client, messagingService) {
  return Promise.all(
    allItems.map((contact) => {
      let usersName = contact.name;
      return client.messages.create({
        body: `Hey ${usersName}! ${message}`,
        messagingServiceSid: messagingService,
        to: contact.key,
      });
    })
  );
}

// Main Function
exports.handler = async function (context, event, callback) {
  // Placeholder for users message
  let message;

  //Twilio and Deta, Etc Const
  const client = require("twilio")(context.ACCOUNT_SID, context.AUTH_TOKEN);
  const deta = Deta(context.DETA_PROJECT_KEY);
  const db = deta.Base("users2");
  const messagingService = context.MESSAGING_SERVICE;

  // From Phone Number
  const from = event.UserIdentifier;

  // Parse memory
  const memory = JSON.parse(event.Memory);

  // Fetch all items from database and return total number of contacts.
  // Update relavent variables
  const allItems = await FetchDB(db);
  const numberOfContacts = allItems.length;

  // Figure out if message came from short circuit broadcast or normal
  if (memory.triggered) {
    message = memory.message;
  } else {
    message =
      memory.twilio.collected_data.broadcast_message.answers.message_input
        .answer;
  }

  // Check if verified and set name.
  const currentUser = await db.get(from);

  // Get the current users name or set a default value
  let userName = currentUser.name || "friend";

  // Determine if user is an authorized broadcaster
  if (from === context.BROADCAST_NUMBER) {
    // Decide if the sending of a message should be cancelled.
    if (message.toLowerCase() === "c" || message.toLowerCase() === "cancel") {
      // Return Callback and end task
      callback(null, {
        actions: [
          {
            say: `${userName}, you have canceled your request and no messages have been sent.`,
          },
          {
            listen: false,
          },
        ],
      });
    }
    // Move forward with sending a message.
    else {
      // Send message to users in database and send success message to broadcaster.
      await SendMessages(allItems, message, client, messagingService);
      return callback(null, {
        actions: [
          {
            say: `${userName}, your broadcast has successfully sent to ${numberOfContacts} contacts.`,
          },
          {
            listen: true,
          },
        ],
      });
    }

    // The user is not authorized so return this.
  }
  return callback(null, {
    actions: [
      {
        say: "You are not authorized to broadcast.",
      },
      {
        listen: false,
      },
    ],
  });
};

我在这里所做的是将FetchDB 更改为仅将db 作为参数,然后创建一个本地allItems 变量来收集所有联系人,然后返回它们。

async function FetchDB(db) {
  let res = await db.fetch();
  let allItems = res.items;

  // continue fetching until last is not seen
  while (res.last) {
    res = await db.fetch({}, { last: res.last });
    allItems = allItems.concat(res.items);
  }
  return allItems;
}

然后在函数的主体中调用它来分配一个局部变量。我还用一个简单的赋值替换了ReturnNumberOfContacts 函数。

  const allItems = await FetchDB(db);
  const numberOfContacts = allItems.length;

您可能需要考虑的一件事是在此功能期间您尝试向多少联系人发送消息。您需要注意一些限制。

首先,Function execution time is limited to 10 seconds 因此,如果您想为此使用 Twilio 函数,您需要确保可以在这段时间内加载和发送所有消息。

此外,您可以与 Twilio API 建立的并发连接数也有限制。该限制过去是每个帐户 100 个连接,但现在可能会有所不同。像在 JavaScript 中一样发送异步 API 请求时,平台将尝试尽可能多地创建与 API 的连接,以便异步触发所有请求。如果您尝试向此处发送消息的联系人超过 100 个,这将很快耗尽您可用的并发连接,并且您将收到 429 错误。您可以选择使用队列,例如p-queue,以确保您的并发连接数永远不会太高。在这种情况下,问题在于处理队列需要更长的时间,这使我回到了最初的 10 秒函数执行限制。

所以,我认为上面的代码现在理论上可能可行,但在实践中使用它可能还有其他问题需要您考虑。

【讨论】:

  • philnash,非常感谢您的帮助。该代码有效并帮助我获得了概念证明。该系统与 3 个联系人按预期工作,但显然我担心您指出的问题。我需要这个系统在至少 1500 个联系人(最好是 5000+)上按预期执行。我的想法是将代码的实际发送部分托管在 aws lambda 之类的东西上,它最多可以有 15 分钟的超时时间。但是如果你有机会,我的直接问题是,你认为这段代码的断点是什么?感谢您的帮助,帮助很大。
  • 当您说“此外,您可以与 Twilio API 建立的并发连接数存在限制。该限制过去是每个帐户 100 个连接,但现在可能会有所不同。” -- 这是每个帐户还是每个子帐户?
  • 我不确定并发性,因为不再发布限制。最好的做法是observe the response headers 并在收到 429 响应时实施重试和指数回退。
  • 对于发送数千条消息,最好不要在单个 Function/Lambda/任何平台内进行。如果它们对每个用户来说是不同的消息,那么我建议尝试设置一个作业队列,该队列可以一次处理一个 API 请求,并通过为队列提供服务的工作人员数量来控制并发性。
猜你喜欢
  • 2019-05-11
  • 2017-07-04
  • 2018-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多