微信学习监督机器人

一、 简要介绍

二、所基于的环境和基础

三、需要设计的内容

​ 在前面的资源搜集以及整理中中,已经完成了机器人和微信之间的接口工作,现在只需要设计和编写出一个对象的函数,可以输入两个参数(目前只有两个),一个是接收到的字符串语言信息,第二个接收到这段字符串语言信息的时间数据。我们需要做的就是将下面的要求完成,并且使用一个对象的函数来达到下面的目标

四、大致设计框架

1、状态设计板块

2、积分设计板块

五、大致设计思路

​ 目前计划设计出两种类来实现目的。

1、玩家类设计思路

(1)变量设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Player:
with open('../config/config_report.json', 'r') as file:
phrases = json.load(file)

def __init__(self, id, name):
self.id = id
self.name = name
self.status = "extraordinary_sleep_end"
self.reputation_points = 100 # 信誉积分
self.scholar_points = 0 # 学者积分
self.reputation_star = 0 # 信誉星分
self.reputation_star_level = 0 # 信誉积分的星级
self.report_times = 0 # 玩家报告次数的记录
self.is_report_must = [] # 玩家是否完成必须报告次数的记录
self.status_start_time_str = "" # 玩家的状态开始时间
# 根据配置文件来确定玩家必须完成报告次数变量的定义
for i in Player.points["reputation_points"]["must_report_phrase"]:
self.is_report_must.append(0)

(2)信誉分处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 结算玩家的信誉积分
# 当时间为23:59的时候运行,此时会根据玩家的报告次数和是否完成必报告内容进行信誉积分和信誉星分的结算
def reputation_points_settlement(self):
is_finish_must = True
# 首先查看玩家是否完成必完成项目
for i in self.is_report_must:
if i == 0:
is_finish_must = False

# 查看当天的报告次数是否达到规定的数量
penalty_report_point = self.report_times - Player.points["reputation_report"]["report_threshold"]
if penalty_report_point > 0 and is_finish_must:
# 达到报告次数的要求
if self.reputation_points == Player.points["reputation_report"]["max_points"]:
# 如果信誉积分是满分,将会奖励一定的信誉星分
self.reputation_star += Player.points["reputation_report"]["report_spill_stars"]
else:
# 如果此时信誉积分不是满分
if self.report_times >= Player.points["reputation_report"]["report_bonus_threshold"]:
# 如果报道次数满足奖励的分数
self.reputation_points += Player.points["reputation_report"]["bonus_point"] * Player.points["reputation_report"]["bonus_threshold_multiplier"]
else:
self.reputation_points += Player.points["reputation_report"]["bonus_point"]
if self.reputation_points > Player.points["reputation_report"]["max_points"]:
# 如果超过设定的最大信誉积分,将会将信誉积分固定到最大积分上
self.reputation_points = Player.points["reputation_report"]["max_points"]
else:
# 如果没有达到报告的次数或者没有完成规定的必须完成的任务
penalty_report_point -= Player.points["reputation_report"]["must_report_missing_penalty"]
if penalty_report_point < Player.points["reputation_report"]["max_deduction"]:
# 如果超过设定的最大信誉积分扣除,将会将扣除部分固定在最大扣除积分上
penalty_report_point = Player.points["reputation_report"]["max_deduction"]
self.reputation_points += penalty_report_point
self.reputation_star += Player.points["reputation_stars"]["penalty_missing_report"][self.reputation_star_level]

# 处理结束后,需要结算某些数据
self.reputation_star_level = self.get_level()
self.report_times = 0
for i in range(len(self.is_report_must)):
self.is_report_must[i] = 0

2、监管者类设计思路

(1) 丰富的配置文件自定义

  • 可以用自己的配置文件来定义其他配置文件的路径,如命令语录配置、报告语录配置、播报语录配置、状态提示语录
  • 可以用自己的配置文件来定义多种交流方式,如,只有用户在群聊的交流前面增加"@莫名的机器人"这个字符串,机器人才会处理这个消息
  • 可以用自己的配置文件来定义最高权限者的wxid,因为有一些命令是只有最高权限者才可以发布的
  • 可以通过自己的配置文件来定义用户状态的类型与其对应的分支。目前有两个大的状态分支:普通状态和非普通状态,在非普通状态下有五个分支状态,分别为睡觉、限制、学习、娱乐、停播。 在前面的五个状态分支中,限制状态的分支有上课、实习,娱乐状态的分支有游戏、视频。但是,由于类的分支比较多,具体计算用户的学者积分的时候需要使用哪个状态的字符串,也是十分值得研究的。所以,以下是目前的设计中所有的状态和其对应的积分计算的依据状态。
    • 普通状态:ordinary。依据状态:ordinary
    • 睡觉状态:extraordinary_sleep。依据状态:extraordinary_sleep
    • 上课状态:extraordinary_restriction_classing。依据状态:extraordinary_restriction
    • 上班状态:extraordinary_restriction_working。依据状态:extraordinary_restriction
    • 学习状态:extraordinary_learning。依据状态:extraordinary_learning
    • 游戏状态:extraordinary_entertainment_game。依据状态:extraordinary_entertainment
    • 视频状态:extraordinary_entertainment_video。依据状态:extraordinary_entertainment
    • 停播状态:extraordinary_departure。依据状态:extraordinary_departure
  • 可以用自己的配置文件定义数据存储的位置
  • 同时拥有一定能力的编辑配置文件的能力,因为需要区分出参与的用户和不参与的用户,需要程序可以自主编辑参与的用户

(2) 变量的设计思路——更多的灵活度

  • 有一个管理群聊字典变量,字典的key对应着群聊的id,字典的value对应着一个这个群聊里面参与玩家的字典,这个字典是key为玩家的wxid,value是玩家对象本身。
  • 有一个权限者的字典变量,这里暂时设定有3个等级的权限,key为用户的wxid,value为用户的权限等级,最高等级为0,其次为1,最低为2。
    1. 权限等级为0的用户,可以对机器人实施任何命令,可以通过施展命令为权限等级为2的用户提升到权限等级1。
    2. 权限等级为1的用户,实际上是参与游戏的玩家,视为被机器人管理的范围。可以使用一些命令和所有报告来与机器人互动。机器人将会将其加入对应群聊的玩家字典组。
    3. 权限等级为2的用户,是没有参与游戏的玩家,机器人不会记录所有关于ta的信息。
  • 有一个日志字典,字典的key对应群聊id,字典的value对应着一个列表,列表里面的每个元素都是一个列表,这个最小单位的列表的数据内容有:时间戳字符串、日志标签、用户id、用户名字、用户当前状态、用户的信誉积分数量、用户的信誉星分数量、用户的学者积分数量、用户交流内容、机器人回应的字符串内容。
  • 有一个日期的字符串变量。这个日期就是当前日志字典里面记录的日期。这个变量每日都会更新,更新后会同样将日志字典里面的最小单位的列表数据内容全部清空,后续将会在这个列表数据里面记录对应更新日期后的日志信息。
  • 有一个报告语录字典。当玩家与机器人进行交互的时候,如果交互的内容存在于报告语录中,则程序可以判断玩家需要与机器人进行交流。这个报告语录由配置文件所定,为类的公有变量。状态字符串为key,报告字符串列表为value。
  • 有一个命令语录字典。当权限为0的管理者与机器人进行交互的时候,如果交互的内容存在于命令语录中,则程序可以判断管理者需要与机器人进行一些配置上的交互。操作字符串为key,操作命令字符串列表为value。
  • 有一个播报语录字典。当程序判断有用户和机器人进行交互的时候,机器人将会调动里面的内容进行交互。key为播报性质字符串,value为播报内容字典,播报内容字典里面的key是播报的内容,value是播报出来的概率。

(3)方法设计思路——更多的接口预留

a.机器人的管理字典变量的数据保存和读取。
  • 需要有一些方法可以保存和管理群聊字典变量里面的数据。

    1. 本地数据保存文件的保存位置的查询方法。首先通过配置文件,知道存储数据的文件夹位置,根据群聊字典变量里面有多少个键值对,查询该路径下是否存在与key同样名称的json文件,如果有不需要新建,如果没有则需要新建一个与key对应名称的json文件。
    2. 本地数据保存的方法。通过该方法,可以将群聊字典变量里面的所有键值对一个json的形式保存数据。对于一个文件,需要保存所有的玩家数据信息,一个玩家的数据信息有:wxid、name、status、reputation_points、scholar_points、reputation_star、reputation_star_level、report_times、is_report_times、status_start_time_str,数据类型分别为字符串、字符串、字符串、整数、整数、整数、整数、整数、装有整数的列表、时间戳字符串。这种保存是覆盖性的。
    3. 本地数据的更新方法。通过该方法,可以在玩家实例的某个数据发生变化后,机器人可以准确修改本地数据保存的json文件里面对应的数据,而不是全部都重新保存一遍。这样除了可以提高程序的运行效率,也可以保证在程序意外中断的时候,用户的数据不至于全部丢失,因为所有的数据都是实时更新并保存到本地中的。这种保存不是覆盖性的
    4. 本地的数据字典的读取方法。如果发生了一些意外使得程序停止运行,但是由于代码的运行逻辑就是实时数据保存,所以所有的数据都完整的保存在原地,那么当程序重新运行的时候,那么可以直接通过读取配置文件设定的路径下所有json文件的信息就可以直接复原出程序停止前的所有群聊字典里面的玩家数据信息。
  • 实际上,不需要特意的方法去将数据保存权限者的变量,因为等级为0的权限者直接在配置文件中定义了,而权限为1的权限者全部在管理群聊字典变量中,对于权限为2的权限者,实际上根本不需要记录。

  • 需要有一些方法将日志变量字典的数据保存。

    1. 一天的日志数据保存文件的保存位置的查询方法。根据配置文件的内容,知道存储日志数据的文件夹位置,根据目前的日期字符串变量,查询在该文件夹下时候存在与当前日期字符串变量同名的文件夹,如果没有则需要新建一个文件夹。
    2. 所有群聊的一天的日志的保存位置的查询方法。在查询或者新建对应日期的文件夹后,进入对应的文件夹中,日志字典里面的所有键值对都对应一个json文件,每个json文件的名称都由所有的key命名,程序需要先查询该路径下是否存在这个文件,如果没有则新建。
    3. 一个群聊的一天日志的保存方法。对于日志字典里面一个群聊id对应的日志列表,需要完整将日志列表的信息保存到本地的对应保留好的位置中。这种保留是覆盖性的。
    4. 一个群聊的一天的日志更新方法。与前面保存群聊字典数据不同,日志数据的更新是线性的,即每当一个群聊的日志列表里面的数据更新的时候,直接在本地对应的json文件里面增加后面的内容即可。
    5. 本地的日志字典的读取方法。如果发生了一些意外使得程序停止运行,但是由于代码的运行逻辑就是实时日志信息保存,所以所有的日志数据都完整的保存在原地。那么当程序重新运行的时候,如果检查到当前时间的日期的数据存在,那么就可以将json文件里面的所有数据信息直接读取到程序的日志字典中。但是如果检查到当前的日志的数据不存在(不可以排出代码隔天后才能正常运行的情况),所以需要新建一个当前日期字符串同名的文件夹和在该文件夹里所有群聊对应的json文件,然后此时的日志数据字典里面键值对中所有的日志列表数据肯定是空的,因为当天并没有所有群聊的日志信息,但是不同担心,后续会不断将日志更新到不同群聊对应的json文件中。
    6. 本地的日志字典的切换存储位置方法。如果发生了日期更替,即时间来到了第二天,此时对应的日期字符串变量也发生了改变,实际上处理思路可以和前面一样。那就是清空当前的日志数据字典里面键值对中所有的日志列表数据,然后新建一个与当前日期字符串同名的文件夹,然后再在这个文件夹下面新建所有群聊对应的日志json文件,然后等待操作日志的更新即可。
b.机器人的实现学习管理需要的方法
  • 交互接口方法。当在一个群聊中有人发言时,就会调用机器人端的功能时,机器人首先会判断发言的信息是否为互动型信息,即发言者的信息前面有特定的前缀(这个前缀由配置文件定义)。如果有特定的前缀,则鉴定为交互,如果鉴定为交互,此时外界会输入四个变量,一个是发言者所在的群聊id,一个是发言者的id,一个是发言的内容,一个是发言的时间戳信息。

    1. 首先通过id,检查发言者的权限等级是否为2。

    2. 如果为2,则只会根据概率返回播报内容字典中对应情景的内容(key:player_permissions_insufficient)。

    3. 如果不是2,则需要检验发言者的内容是否存在于报告语录和命令语录字典中。

    4. 如果检验发言者的内容既不存在于播报语录字典中,也不存在于命令语录字典中,那么只会根据概率返回播报内容字典中对应情景的内容(key:report_not_find)。

    5. 如果检验发言者的内容存在于播报语录字典中,系统将视为玩家与机器人之间的交互,这种交互涉及到玩家的状态变化。需要进一步检验玩家所说的内容是否合法。(对于一个状态,有对应的开始语言和结束语言,程序需要分辨出来且不能搞混)

      • 程序需要判断这个语言属于具体哪个非普通状态的开始语言还是结束语言。(普通状态没有专门的开始语言和结束语言,因为非普通状态的开始(结束)语言实际上就是普通状态的结束(开始)语言)

        • 如果玩家的交互语言是某个非普通状态的开始语言(需要清楚到最后一个状态分支,例如:玩家如果说的是游戏状态的开始语言,系统不能将玩家判断为娱乐状态,而是直接判断为娱乐状态的分支下面的游戏状态)

          • 机器人需要判断玩家的状态是否就是这个对应的非普通状态(不能重复进入状态)。
          • 如果不是,则说明玩家需要从前面一个非普通状态转化为另外一个非普通状态,使用发言者所在的群聊id和发言者的id查询到该玩家的对象,然后调用他的状态转化方法即可,并根据播报内容字典返回特定的情景的内容即可。(返回内容不固定,根据玩家结束的是哪个状态而定)。在调用方法之前,依旧需要判断一件事,那就是如果玩家的信誉积分低于60(实际上不是60,这个需要根据配置文件所定),且玩家想要进入的状态是娱乐状态和停播状态,这是系统不允许的,所以只需要根据概率返回播报内容字典中对应情景的内容即可(key:reputation_points_too_low),不需要调用玩家对象的方法。
          • 如果是,说明玩家可能搞混了自己的状态,那么只根据概率返回播报内容字典中对应情景的内容即可(key:report_not_find)。
        • 如果玩家的交互语言是某个非普通状态的结束语言(需要清楚到最后一个状态分支,例如:玩家如果说的是游戏状态的开始语言,系统不能将玩家判断为娱乐状态,而是直接判断为娱乐状态的分支下面的游戏状态)

          • 机器人需要判断玩家的状态是否就是这个对应的非普通状态(防止玩家说错导致其他状态被结束)。
          • 如果不是,则会只根据概率返回播报内容字典中对应情景的内容即可(key:report_not_find)。
          • 如果是,那么就会调用玩家对象的转化方法,并根据播报内容字典返回特定的情景的内容即可。(返回内容不固定,根据玩家结束的是哪个状态而定)

    6. 如果发言者的内容存在于命令字典中,那么只需要执行对应的操作即可。(每个命令字典里面都对应一个操作函数)

需要完成的内容

  • 搞清楚计算学者积分的的status的名称和具体计算学者积分的名称,即extraordinary_entertainment_game和extraordinary_entertainment究竟是怎么计算的。需要重新审视Player类
  • 搞清楚如何计算必完成事项的实现原理
  • 搞清楚结算当日的积分应该如何表现,是直接输出,还是作为一段数据保存,存储到哪个文件夹
  • 如何进行多线程计算???
  • 为玩家的报告和交互内容添加到日志中
  • 最后一步,就是如何进行线程设计?如何做到让机器人自主发言

首先,给类创建一个字典数据:

在列表数据中有很多变量

需要给我的类添加一个时间字典,一个时间列表:

列表里面装有按照从早到晚的需要完成的事的时间变量字符串,

字典的key实际上列表中的时间变量,value是需要完成的事的触发事件数据的列表(事件可能有多个),一个事件数据是一个字典,字典里面只有wx_id, room_id,e, event_type_broadcast这四个键,对应的值都是字符串。

需要有一个函数,检测时间。输入参数就是时间字符串,然后检查时间列表里面的头一个时间字符串,如果输入参数的时间大于或者等于时间列表的头一个字符串,那么将时间列表的头一个时间字符串弹出,并且插入到一个新的时间列表中,这个新的时间列表暂时称之为要做的时间列表。然后继续遍历时间列表的下一个头时间字符串,依次类推,直到这个时间字符串小于时间列表的头一个字符串。最终,如果得到的要做的时间列表是空 的,则直接返回,如果不是空的,则要调用下面的函数

还有一个函数,完成对应时间的操作。输入参数是要做的时间字符串列表,找到字典里面所有对应时间字符串列表的事件数据列表,将所有要做的时间列表里面的值对应时间字典里面的所有事件数据列表里面的所有字典,作为参数调用一个完成时间事件的函数(这个函数还未具体定义),完成后从触发事件数据的列表中弹出这个对应时间数据字典,如果在时间字典中,触发事件数据的列表空了,那么就可以在时间字典中删除这个键值对。

还要有一个函数,就是新增时间事项。输入参数to_do_time_strtime_event_dict这两个数据,首先查询to_do_time_str是否存在于时间列表中,如果有则直接将time_event_dict插入到时间字典对应的value里面的列表中。如果不存在与时间列表中,就需要将to_do_time_str按照时间先后顺序插入时间列表中,然后再将time_event_dict插入一个空列表中,作为时间字典的to_do_time_str对应的value