如何使用Twilio,Python和Google工具自动执行婚礼

文章的作者讲述了他如何设法运用编程技巧来自动化自己的婚礼活动的例行流程。

对于大多数人来说,2016年9月3日是最常见的星期六,但在我的记忆中,这一日期将永远存在,因为正是这一天,我和我的妻子举行了婚礼。

图片

策划婚礼时,您需要考虑很多方面:食物,装饰和装饰,台灯(是的,与装饰分开),鲜花,住客住宿,交通,娱乐和场地选择。 通常,在计划婚礼时,您会遇到许多未知数,但是我确信一件事:没有一堆完整的列表,嵌套列表直到最后都不会结束,任何婚礼都无法做。 在我眼前浮现的清单越多,我就开始思考如何改善准备过程。 效率极低,所有工作都是手动执行。 我确信技术肯定可以帮助改善至少某些方面。

得知邀请人们参加婚礼很昂贵(每人超过380磅),您可能会感到惊讶。 首先,您需要发送带有日期和简短通知的初步邀请,然后才是-具有完整功能的详细邀请,然后发送通知。 而且,所有这些都是通过邮件发送的,这意味着速度很慢。 试图“抓住”被邀请者并从他们那里得到答案,即他们是否想带免费的食物和饮料去度假(尽管看起来,谁不想?)需要很多时间。 最后,发出邀请并不环保,因为纸卡是一次性的事情,它们很快就会被忘记并且对任何人都变得毫无用处。

但是回到列表。 我们将客人分为几组:

  1. 您想在节日上看到的那些
  2. 回答第二个邀请的人要求答复
  3. 接受邀请的人
  4. 那些接受邀请并选择食物的人

但是我喜欢这些列表:它们具有一些预定义的要求,这些要求使它们成为自动化的重要对象。

瓶中的消息


我确定,不管年龄多大,所有潜在客人都拥有手机,这意味着Twilio的时机已经到了。 如果愿意,可以安全地跳过此处给出的代码,因为它始终在相应的GitHub存储库中可用。

短信作为沟通渠道非常适合我的需求。 我可以设置邮件群发,快速有效地处理响应。 在制作该产品的第一个工作草图并考虑数据库选项之后,我尝试做一些简单的事情,可以轻松共享并且不想花很多时间在外观上。 最后,我遇到了gspread python库,该库使我可以读取和写入google表 。 这不是最快的方法,而是灵活的选择,它使访问表和读取结果变得容易。

对于第一个提示,我创建了一个包含三列的表:

  • 名称
  • 电话号码
  • Confirmation_status(确认状态)
  • 联系人详细状态
  • Message_count(发送给来宾的消息数将来派上用场)

完成基本数据输入后,我通过gspread运行了该列表,该列表向拥有手机号码的每个访客发送了一条SMS:

import json import time import gspread from oauth2client.client import SignedJwtAssertionCredentials from twilio.rest import TwilioRestClient #      #     json,    json_key = json.load(open('.json')) scope = ['https://spreadsheets.google.com/feeds'] credentials = SignedJwtAssertionCredentials(json_key['client_email'], json_key['private_key'].encode(), scope) gc = gspread.authorize(credentials) wks = gc.open("wedding_guests") #     workbook- wks_attendees = wks.get_worksheet(0) #     ACCOUNT_SID = 'TWILIO_ACCOUNT_SID' AUTH_TOKEN = 'TWILIO_AUTH_TOKEN' client = TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN) #    ,   range    for num in range(2, 60): print "sleeping for 2 seconds" time.sleep(2) #         guest_number = wks_attendees.acell('B'+str(num)).value guest_name = wks_attendees.acell('A'+str(num)).value Message_body = <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\n\n</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2709</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span><span class="pl-s"><span class="pl-pds">"</span> Save the date! <span class="pl-pds">"</span></span><span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2709</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span><span class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\n\n</span>Lauren Pang and Thomas Curtis are delighted to invite you to our wedding.<span class="pl-cce">\n\n</span>Saturday 3rd September 2016. <span class="pl-cce">\n\n</span>Colville Hall,<span class="pl-cce">\n</span>Chelmsford Road,<span class="pl-cce">\n</span>White Roding,<span class="pl-cce">\n</span>CM6 1RQ.<span class="pl-cce">\n\n</span>The Ceremony begins at 2pm.<span class="pl-cce">\n\n</span>More details will follow shortly!<span class="pl-cce">\n\n</span>Please text YES if you are saving the date and can join us or text NO if sadly, you won't be able to be with us.<span class="pl-cce">\n\n</span><span class="pl-pds">"</span></span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2B50</span><span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-k">u</span><span class="pl-pds">"</span><span class="pl-cce">\u2764</span><span class="pl-pds">"</span></span>, if not guest_number: #      print guest_name + ' telephone number empty not messaging' wks_attendees.update_acell('E'+str(num), '0') else: print 'Sending message to ' + guest_name client.messages.create( to="+" + guest_number, #  +   e.164 from_="", #     Twillio body=message_body, ) wks_attendees.update_acell('E'+str(num), int(wks_attendees.acell('E'+str(num)).value) + 1) # increment the message count row else: # else-  print 'finished' 

而且由于文本SMS通常看起来有些无聊,因此我添加了一些Unicode来为其增添趣味。 这是给幸运的客人传达的信息:

图片

接下来,我使用Flask作为Web服务器,将对Twilio Messaging的URL请求指向/消息,并添加了简单的if-check来解析响应:

 @app.route("/messages", methods=['GET', 'POST']) def hello_guest():  if "yes" in body_strip:    #      confirmation_status    wks_attendees.update_acell("F"+str(guest_confirmation_cell.row), 'Accepted')  #    « »       resp.message(u"\u2665" + "Thanks for confirming, we'll be in touch!" + u"\u2665")  # r ,    elif "no" in from_body.lower():    #    « »       wks_attendees.update_acell("F"+str(guest_confirmation_cell.row), 'Declined')    #       resp.message("Sorry to hear that, we still love you though!")  else:  #  ,        resp.message("You sent a different keyword, we need a yes or a no, you sent: "+           from_body)  return str(resp) 

图片图片

第一封邮件是2月19日上午8:37发送的,第一封确认信是在稍后的8:40收到的。 到9:38时,我已经收到23条确认信息,也就是说,我的口袋里有32%的答案! 批量邮寄开始2天后,已有58%的客人确认参加。 尽管取得了明显的成功,但我未来的妻子尚未对我的婚礼邀请短信服务留下100%的印象,因此我决定为该应用程序添加更多功能。

统计! 我可以创建一个最新的来宾清单,并根据要求提供它,从而给我未来的新娘一个即时的反馈。 事实证明,代码非常简单,因为我之前已经在表中设置了一些最简单的计数器,因此归结为获取单个单元格的内容并将其添加到SMS中:

 #    guest_confirmed = wks_attendees.acell('C70').value guest_unconfirmed = wks_attendees.acell('C71').value guest_no_response = wks_attendees.acell('C72').value guest_acceptance = wks_attendees.acell('C73').value elif "numbers" in from_body.lower():  #   ( - ,   )  resp.message("RSVP update:\n\nTotal Accepted: " + guest_confirmed         "\n\nTotal declined: " guest_unconfirmed "\n\nTotal no response: "+         guest_no_response + "\n\nTotal acceptance rate: " + guest_acceptance) 

通过此代码发送的短信示例:

图片

它看起来可能不是很漂亮,但是非常有用。

劳伦现在可以跟踪来宾列表的自动更新这一事实为我们省去了很多麻烦。 结果,我得到了她的广泛集成SMS的批准,并且很快,该工具几乎在所有可能的过程中都得到了使用。 其中一些应用程序很明显,例如,发送关于婚礼网站启动的SMS通知(顺便说一句,在Heroku上完成 ),或者处理婚礼礼物清单以及我今天引以为傲的许多其他解决方案。

光荣盛宴


在整理了被邀请者的名单并收集答案之后,就开始了本课,通常将其推迟到以后-找出客人的喜好。 第一步是发送另一条SMS,告知来宾需要访问该网站并使用google表单选择提供的食物类别。 该表单看起来很普通,但是它填充了包含访问者数据的同一文件。 因此,现在我们既有一个接受邀请的访客表,又有一个填写食物选择表的表。 如果我们在谈论任何普通的自动化工具,我将不得不等待客人慢慢选择他们的菜,但是,我的婚礼是在Twillio的支持下举行的,这意味着我可以以最小的努力得到客人的回应。

有必要用客人的名字核对两张桌子,并在收到新信息后更新所选菜肴的状态。 这需要在代码中添加一些额外的信息,但是一旦完成这些操作,我就可以尽快运行脚本,并接收包含最新信息的SMS:

 import json import time import gspread from oauth2client.client import SignedJwtAssertionCredentials from twilio.rest import TwilioRestClient #     json,    json_key = json.load(open('')) scope = ['https://spreadsheets.google.com/feeds'] credentials = SignedJwtAssertionCredentials(json_key['client_email'],                      json_key['private_key'].encode(),                      scope) gc = gspread.authorize(credentials) wks = gc.open("")  #      wks_attendees = wks.get_worksheet(0)  #   wks_food = wks.get_worksheet(1)  #    ACCOUNT_SID = 'TWILIO_ACCOUNT_SID' AUTH_TOKEN = 'TWILIO_AUTH_TOKEN' client = TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN) #   . -       for num in range(2, 60):  food_guest_name = wks_food.acell('B'+str(num)).value  #        if food_guest_name:    attendees_name = wks_attendees.find(val_food_guest_name).value    attendees_name_row = wks_attendees.find(val_food_guest_name).row    menu_status = wks_attendees.acell("G"+str(attendees_name_row)).value    if food_guest_name == attendees_name:      print      if menu_status == 'Y':  #   ,          print('Skipping')      else:  #    ,           print ('Food sheet name ' + food_guest_name + 'Attendees sheet name ' + attendees_name)        #            wks_attendees.update_acell("G"+str(attendees_name_row), 'Y')    else:      print('nothing found, moving on')      wks_attendees.update_acell('E'+str(num), int(wks.acell('E'+str(num)).value) + 1)  #      else:    #           client.messages.create(from_="",  #  Twillio                to="",  #                  body="Finished processing current meal listnnGuest meals confirmed" + guest_meals_confirmed + "\n\nGuest meals unconfirmed: " + guest_meals_unconfirmed) 

现在,我有了一个准确的客人名单和一个不断更新的菜肴清单,现在可以使用主应用程序将这些统计信息公开显示出来了。 为此,只需将相应单元格的内容添加到SMS答复中:

 #           elif "food" in body_strip.strip():  resp.message("Guest meals decided:" + guest_meals_confirmed + "\nGuest meals undecided: " + guest_meals_unconfirmed + "\n\nMenu breakdown:\n\n" + starter_option_1 +": " + starter_option_1_amount + "\n" + starter_option_2 +": " + starter_option_2_amount + "\n" + starter_option_3 +": " + starter_option_3_amount + "\n" + main_option_1 +": " + main_option_1_amount + "\n" + main_option_2 +": " + main_option_2_amount + "\n" + main_option_3 +": " + main_option_3_amount + "\n" + dessert_option_1 + ": " + dessert_option_1_amount + "\n" + dessert_option_2 + ": " + dessert_option_2_amount) 

图片

事实证明,这项措施非常有用,因为它可以使服务于假期的公司了解我们的进度,并从尚未做出选择的人员那里获得非常实用的信息。 自动化的下一个竞争者是接收来宾响应的过程。 为此,只需要遍历该列表,在其中找到未选择菜肴的“违反者”,并向他们发送消息!

 for num in range(2, 72):  #         print "sleeping for 3 seconds"  time.sleep(3)  #            wedding_guest_number = wks_attendees.acell('B'+str(num)).value  #     wedding_guest_name = wks_attendees.acell('A'+str(num)).value  #     menu_guest = wks_attendees.acell('G'+str(num)).value  if not wedding_guest_number:    print wedding_guest_name+' telephone number empty not messaging'  #   ,     .            wks_attendees.update_acell('H'+str(num), '1')  #         else:    if menu_guest == "N":  #    !      !      print 'Sending message to '+wedding_guest_name      client.messages.create(        to="+" + wedding_guest_number,        from_="",  #   Twillio        body="If you have received this message, you have not chosen your food options for Tom & Lauren's Wedding!\n\nYou can pick your choices via the website, no paper or postage required!\n\nhttp://www.yourwebsitehere.com/food"      )      wks_attendees.update_acell('H'+str(num), int(wks_attendees.acell('H'+str(num)).value) + 1)  #        else:          # else-   print 'finished' 

图片

大日子比我们想象的要快。 我们唯一要做的就是发送最后一条短信,提醒客人基本信息以及需要用雨伞武装自己,这将有助于保护自己免受典型的英国多雨夏天的侵害:

图片

总结


组织婚礼绝非易事。 在任何时候,您似乎都无法控制该事件的许多方面。 通过提供与来宾的直接沟通渠道以及无数工具来跟踪他们的回答并提醒他们进行决定和回答的必要性,自动化绝对使我的生活更轻松。 她帮助我们处理了此类事件中最繁琐的事情之一,并让我们腾出了很多时间,专注于生活中这一重要事件的其他重要组成部分。

为复杂的任务创建可扩展的解决方案从来都不是一件容易的事,即使我的应用程序的最终版本之一也几乎无法应付现场的任务。 最初,我计划开发一个更全面的解决方案,以可视化的方式显示进度,语音集成,减少对CLI脚本的依赖,但是在这场比赛中占了上风。 总的来说,我对一切结果感到满意。 没有完善的通信系统。 您应该始终使用最适合您的受众的频道,无论是短信语音聊天视频还是信号量

如果您想谈论婚礼自动化,请在Twitter上给我发电子邮件。

图片

Source: https://habr.com/ru/post/zh-CN402827/


All Articles