从Python剧院海报的解析器到Telegram机器人。 第二部分



我们将继续介绍用于票务搜索的Telegram机器人-HappyTicketsBot的开发故事,其开头可以在第一部分中找到。

在第二篇中,我将讨论机器人本身,共享代码以及最有可能无法实现的想法。 机器人创建时的大多数功能已经以脚本格式编写,因此主要任务是通过Telegram-messenger建立用户交互界面。 事实证明,其方法不像第一部分那样严格,因此要注意很多代码。

剧透:HappyTicketsBot并没有飞起来去打开外国服务器,它是本地的和俄语的,但是有一天(我相信)它将开始=)

更新:在剧院公众之间共享该机器人之后,他们在媒体上对此进行了报道。 大量用户激增。 在玩了几天游戏之后,“立即将它拾起,它是如何掉下来的”,该机器人飞到服务器上并经历了许多改进。 我很满意=)

1.从头开始


由于在设计Telegram机器人时一言不发,所以我必须从基本文章和教程入手,在网络上有很多文章和教程。 是的,顺便说一下,那时的后端是什么,我也很难想象)) 这套课程成为最有用的信息。 与Telegram交互的模块是pyTelegramBotAPI( github )。

最长的时间是装饰者意识形态的发展,请在本文中阅读有关它们的内容 。 有两个部分,非常容易理解。

2.机器人与用户交互的场景 基本搜寻


如前言和文章的第一部分所述 ,几乎所有解析代码都已准备就绪。 剩下的改变设置搜索参数的方法。 基于此,构建了机器人行为脚本。 用户可用的命令限于以下集合:

  • /查找 -开始新的搜索,
  • /重置 -重置搜索参数并开始一个新的参数,
  • / LastSearch-使用最后一个查询的参数返回结果,
  • / addURL以兴趣添加效果URL来跟踪降价,
  • / checkURL-为感兴趣的表演更新价格,
  • / showURL-列出所有添加到兴趣列表的URL

根据基本搜索脚本/查找,用户从一种状态转移到另一种状态,依次输入过滤器所需的数据。 输入最后一个参数(演示位置)后,将使用全局声明的字典直接分析发布者,其中关键字是用户ID,值是输入的搜索参数。

为了记住用户的状态,他们存储在数据库中。 要使用它,需要使用Vedis模块(键值数据库配置器,请阅读文档 )和Enum(使用枚举,详细信息1,2 )。

在一个单独的配置文件Myconfig.py中,我们设置了bot参数(包括从Telegram接收到的唯一令牌)并列出了用户可以进入的状态。 他们出来了一点。

from enum import Enum token = "4225555:AAGmfghjuGOI4sdfsdfs5656sdfsdf_c" # ,   (  ) db_file = "Mydatabase.vdb" class States(Enum): """   Vedis    ,        (str) """ S_START = "0" #    S_ENTER_MONTH = "1" S_ENTER_PRICE = "2" S_ENTER_TYPE = "3" S_ENTER_PLACE = "4" S_ENTER_URL="5" #       

结果,我们获得了从一个状态到另一个状态的直接状态转换链。



为了存储,我们使用Vedis数据库。 发送消息的用户的初始化始终通过message.chat.id完成。

dbwoker.py文件的代码,它描述与数据库的交互
 from vedis import Vedis import Myconfig as config #      def get_current_state(user_id): with Vedis(config.db_file) as db: try: return db[user_id] except KeyError: #  /     return config.States.S_START.value #  -  #       def set_state(user_id, value): with Vedis(config.db_file) as db: try: db[user_id] = value return True except: print('  !') #   -   return False 


下面是由/ find命令激活的处理程序的示例。 如您所见,在此示例中,没有数据输入-只有状态更改为“ S_ENTER_MONTH”。 在看到输入号码的消息后,用户将其输入并发送一条消息。 收到状态为S_ENTER_MONTH的消息后,将启动下一步。 输入错误时,状态不会改变。

  #   @bot.message_handler(commands=["find"]) def cmd_find(message): state = dbworker.get_current_state(message.chat.id) """    ,         .     .    ,           """ if state == config.States.S_ENTER_MONTH.value: bot.send_message(message.chat.id, "    .   ") elif state == config.States.S_ENTER_PRICE.value: bot.send_message(message.chat.id, "      ,   ") elif state == config.States.S_ENTER_TYPE.value: bot.send_message(message.chat.id, ", -    ,       :( ...") else: #  ""   "0" -   bot.send_message(message.chat.id, "      ") dbworker.set_state(message.chat.id, config.States.S_ENTER_MONTH.value) #   

如果漫游器从用户收到状态为S_ENTER_MONTH的消息,则将启动以下处理程序。 从理论上讲,它也发生在基本搜索脚本的其他阶段。

 @bot.message_handler(func=lambda message: dbworker.get_current_state(message.chat.id) == config.States.S_ENTER_MONTH.value) def user_entering_month(message): if not message.text.isdigit(): bot.send_message(message.chat.id, ",    ") return # 1 num[message.chat.id]=message.text #  if int(num[message.chat.id])>12 or int(num[message.chat.id])<1: bot.send_message(message.chat.id, "     1  12.   ") # 2 return url_list[message.chat.id]=take_url(num[message.chat.id]) #  URL-   if url_list[message.chat.id]==[]: #    bot.send_message(message.chat.id, " ,     .     ") return bot.send_message(message.chat.id, "!      .") dbworker.set_state(message.chat.id, config.States.S_ENTER_PRICE.value) #     

除了标准搜索外,还可以保存有趣的表演。

3.跟踪价格变化


用户可以将URL添加到兴趣列表中,以便在价格下跌时收到警报。 我们记得在基本搜索-S_ENTER_URL中我们仍处于未列出状态。 在

 @bot.message_handler(commands=["addURL"]) def cmd_add_url(message): bot.send_message(message.chat.id, " url,   .  https://") dbworker.set_state(message.chat.id, config.States.S_ENTER_URL.value) #  @bot.message_handler(func=lambda message: dbworker.get_current_state(message.chat.id) == config.States.S_ENTER_URL.value) def user_entering_URL(message): perf_url=message.text user_id=message.chat.id try: add_new_URL(user_id,perf_url) bot.send_message(message.chat.id, '    !') dbworker.set_state(message.chat.id, config.States.S_START.value) #    except: bot.send_message(message.chat.id, 'URL !    !') dbworker.set_state(message.chat.id, config.States.S_ENTER_URL.value) 

要存储列表,请使用.csv文件。 要与之交互,您需要几个功能-编写和阅读并验证价格变化。 如果更改,请通知用户。

  def add_new_URL(user_id,perf_url): WAITING_FILE = "waiting_list.csv" with open(WAITING_FILE, "a", newline="") as file: curent_url='https://'+perf_url text=get_text(curent_url) #   1   minPrice, name,date,typ,place=find_lowest(text) user = [str(user_id), perf_url,str(minPrice)] writer = csv.writer(file) writer.writerow(user) 

价格更新功能代码稍长
 def update_prices(bot): WAITING_FILE = "waiting_list.csv" with open(WAITING_FILE, "r", newline="") as file: reader = csv.reader(file) waitingList=[] for row in reader: waitingList.append(list(row)) L=len(waitingList) lowest={} with open(WAITING_FILE, "w", newline="") as fl: writer = csv.writer(fl) for i in range(L): lowest[waitingList[i][1]]=waitingList[i][2] #   URL  for k in lowest.keys(): text=get_text('https://'+k) minPrice, name,date,typ,place=find_lowest(text) #    1   if minPrice==0: #   minPrice=100000 if int(minPrice)<int(lowest[k]): #   ,    lowest[k]=minPrice #    for i in range(L): if waitingList[i][1]==k: #  -  URL   waitingList[i][2]=str(minPrice) #  bot.send_message(int(gen[i][0]),'   '+k+'    '+str(minPrice)) writer.writerows(waitingList) #     .    ... ... 


结果,通过命令/ checkURL,用户可以获得这样的结果(现在我知道也有必要显示演奏的名称,但是这些是“没动手”系列中的内容)。



好吧好吧 我们可以搜索,我们可以跟踪。 几个朋友开始使用该机器人,我想了解他们是谁以及他们在寻找什么。 将此信息很好地写入日志中。

4.我们在日志中记录活动和错误


日志记录模块将为我们提供帮助。 仅在完成基本搜索的阶段,在处理程序中记录信息,在处理程序中,用户状态从S_ENTER_PLACE传递到S_START。 错误记录又在发生时发生。

关于该模块的工作原理,我不会说太多,所以最好参考外部信息。



记录器说明
 def save_logs(str): loggerInfo.info(str) #    logging.basicConfig(format = u'%(levelname)-8s [%(asctime)s] %(message)s', level = logging.ERROR, filename = u'loggerErrors.log') global loggerInfo loggerInfo = logging.getLogger(__name__) loggerInfo.setLevel(logging.INFO) handler = logging.FileHandler('loggerUsers.log') handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) loggerInfo.addHandler(handler) log = logging.getLogger("ex") 


由于连接断开,该漫游器会定期崩溃,因此捕获到Internet连接错误,并且该漫游器会在10秒后自动重新启动。 但是它并不总是保存,因此我使TeamViewer保持运行状态,以便在必要时提高它。

5.未实现


我们有一个机器人,它取代了脚本的功能,但允许您在Messenger内以方便的形式接收信息。 他满足了我的基本需求。

在周末,有时在晚上,以工作方式进行模块拆卸和编写纤细的处理程序持续了大约一个月。 在此期间结束时,兴趣开始减弱,功能停留在起点。 无法突破使用webhook-ah的原则,然后Telegram被阻止。 在此之前,有计划将后端拉到工作的服务器,但是... vpn不会被放置在这里=)

以下是计划中剩下的内容,其中一些可以并且将在一个疲弱的夏季/冬季晚上实现一次:

  • 大量用户的负载测试。 目前尚不清楚该机器人是否会稳定运行并不会使用户感到困惑。
  • 在演出者日程中出现新表演的通知。 我有很多喜欢的“白兔子”,我无法追踪所有人(但是我想);
  • 通知某类门票的出售情况。 有一个熟人,是第一排摊位的业余爱好者,很难手动抓到。
  • 定期自动检查感兴趣的网址,以降低计时器的价格。 现在,这是在命令上完成的,无法快速设置计时器,因此可以通过简单的方式进行设置;
  • 保留其参观演出的历史 在.csv文件中的某个位置,艺术家的名字对等,以免丢失;
  • 搜索给定的票证类别。 不仅要设置价格,还要设置部门(底层等);
  • 将一切都转化为爱丽丝的技能。 为什么不呢?
  • 制作具有相同功能的移动应用程序。 为什么不呢?

莫斯科大剧院有个电话。 为了赶上Nureyev的门票,但我无法在两个晚上拿起html海报,因此它也被列入未实现的名单。

合计


懒惰是进步的动力,它阻止了他。 将该机器人上载到第三方服务器并没有发生任何事情;不过,这需要在Web领域具有更广泛的能力和知识。 事实证明该项目很有趣,它使我们能够学习更好的Python,从另一个角度来看(除了通常的机器学习),而且还以低廉的价格介绍了剧院里许多美好的夜晚。 多亏了他,他才得以完成任务。

无论我如何尝试,本文仍然有很多代码和一些文本。 我将很高兴解释评论中难以理解或很少描述的内容=)

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


All Articles