基于Nonebot的班级消息管理机器人

项目介绍以及关于Nonebot

大二的时候,我在面对学校各种繁杂且混乱通知感到乏力,我想到开发一款以消息和时间作为基本数据,对qq(或者微信)中各种通知群的通知消息进行识别并进行管理。而这里的管理则是对消息进行重现,即在消息的deadline前进行多次提醒。
关于nonebot ,NoneBot 是一个 Python 的异步 QQ 机器人框架,允许开发者们以插件化的形式对 QQ 机器人收到的消息进行解析和处理来完成具体的功能。点击这里进入nonebot官网

Nonebot 以及 Python jdk的安装。

首先市面上各种qq、微信机器人都相当多,大可不必一定要使用Nonebot。而对于Nonebot开发者们可以使用各种各样的开发语言进行拓展。我这里使用到了python语言。pypi 最新的Python开发包

机器人项目成品

项目文件展示

这和机器人的版本更新有关,不一定是这样的。我的文件夹放的目录如下
介绍的两个py文件:
CQPlusHandler 中断处理函数(负责消息转发的基本流程控制)
YB_searchtime 时间处理函数 (其中有函数传入字符串,返回datetime接口)

文件目录展示

CQPlusHandler 中断处理函数

首先中断函数处理,当机器人收到消息触发中断方式。这个文件处理消息的时候,进行一系列的消息处理。

首先最基本的定时发送功能
其次 在此之上的识别消息所带时间信息,并定时发送
以下是源代码

# -*- coding:utf-8 -*-
import cqplus,YB_searchtime,re,time,_thread, os ,random
from datetime import datetime,timedelta
from threading import Thread
#配置基本信息
revprivate = ()#监听qq名单
sendprivate = []
revgroup = []#要监听的qq消息群
sendgroup = [917292963] #要发送的qq消息群
f = open('msgdata.txt',mode='r+', encoding='UTF-8-sig')
msgdata = f.read()
msgdata = msgdata.split("\n" or '\\ufeff')
ksdtime = datetime.strptime("2019-12-23","%Y-%m-%d")




class MainHandler(cqplus.CQPlusHandler):
    def handle_event(self, event, params):
        #self event为收到消息事件 字符串
        #params 为 事件的参数 dict 类型
        if event=="on_private_msg":
            if re.match("查询最近的事",params['msg']):
                  #  self.api.send_private_msg(params['from_qq'], "触发了")
                for temp in msgdata[::2]:
                    self.api.send_private_msg(params['from_qq'], temp)
        if event=='on_group_msg':
            if params['from_group'] in revgroup and params['from_group'] != 916367320:
                self.api.send_group_msg(916367320, params['msg'])
                for target_time in YB_searchtime.time_extract(params['msg']):
                        msgdata.append(target_time.strftime("%Y-%m-%d %H:%M:%S"))
                        msgdata.append(params['msg'])
                        f.seek(0)
                        f.truncate()
                        f.seek(0)
                        for i in msgdata:
                            if (i!=""):
                                f.write(i+"\n")
                        f.close()
        for i in msgdata:
            if ((msgdata.index(i)+1)%2 and i!=""):
                try:
                    target_time = datetime.strptime(i, "%Y-%m-%d %H:%M:%S")
                    if target_time <= datetime.today():
                        a = msgdata.index(i)
                        del msgdata[a]
                        for temp in sendgroup:
                            pass
                            #self.api.send_group_msg(temp,msgdata[a])
                        del msgdata[a]
                        f.seek(0)
                        f.truncate()
                        f.seek(0)
                        for temp in msgdata:
                            if (i!="" and i!='\n'):
                                f.write(temp+"\n")
                        f.close()
                except:
                    f.close()

        # 这一部分为定时发送功能
        q = open('D:\\机器人项目\\酷Q Air\\app\\cn.muxiaofei.coolq_sdk_x\\senddata.txt', mode='r+',
                 encoding='UTF-8-sig')
        lz = open('D:\\机器人项目\\酷Q Air\\app\\cn.muxiaofei.coolq_sdk_x\\sendlz.txt', mode='r+',
                  encoding='UTF-8-sig')
        lz = lz.read().split("\n" or '\\ufeff')
        senddata = q.read()
        senddata = senddata.split("\n" or '\\ufeff')
        for i in senddata:
            if ((senddata.index(i) + 1) % 2 and i != ""):
                target_time = datetime.strptime(i, "%Y-%m-%d %H:%M:%S")
                target_time = datetime.strptime(i, "%Y-%m-%d %H:%M:%S")
                if target_time <= datetime.today():
                    a = senddata.index(i) + 1
                    for temp in sendgroup:
                        self.api.send_group_msg(temp, senddata[a])
                        if "每日一句" in senddata[a]:
                            self.api.send_group_msg(temp, "离考试还有:"+str((ksdtime-datetime.today()).days)+"天。\n"+lz[random.randint(0, 21)])
                    target_time = target_time + timedelta(days=1)
                    senddata[a-1] = target_time.strftime("%Y-%m-%d %H:%M:%S")
                    q.seek(0)
                    q.truncate()
                    q.seek(0)
                    for temp in senddata:
                        if (i != "" and i != '\n'):
                            q.write(temp + "\n")
                    q.close()



YB_searchtime 时间处理函数

这是整个插件项目的重头工作。我们需要将一段自然语言中提取出时间信息比如说有(‘明天下午’,‘今天两点’,‘两天后’,‘10月份底’,‘10月29日’)等等
如果您不只是想要时间提取的代码,我将细述这种算法代码的运行原理,如果您学过编译原理,那么这种算法和编译原理中提取关键字阶段的工作十分相似。
下来我把我使用的代码(对原来的代码有完善和修改,我找不到原创的作者)

# -*- coding:utf-8 -*-
import re
from datetime import datetime, timedelta
import jieba.posseg as psg

UTIL_CN_NUM = {
    '零': 0, '一': 1, '二': 2, '两': 2, '三': 3, '四': 4,
    '五': 5, '六': 6, '七': 7, '八': 8, '九': 9,
    '0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
    '5': 5, '6': 6, '7': 7, '8': 8, '9': 9
}

UTIL_CN_UNIT = {'十': 10, '百': 100, '千': 1000, '万': 10000}


def cn2dig(src):
    if src == "":
        return None
    m = re.match("\d+", src)
    if m:
        return int(m.group(0))
    rsl = 0
    unit = 1
    for item in src[::-1]:
        if item in UTIL_CN_UNIT.keys():
            unit = UTIL_CN_UNIT[item]
        elif item in UTIL_CN_NUM.keys():
            num = UTIL_CN_NUM[item]
            rsl += num * unit
        else:
            return None
    if rsl < unit:
        rsl += unit
    return rsl


def year2dig(year):
    res = ''
    for item in year:
        if item in UTIL_CN_NUM.keys():
            res = res + str(UTIL_CN_NUM[item])
        else:
            res = res + item
    m = re.match("\d+", res)
    if m:
        if len(m.group(0)) == 2:
            return int(datetime.datetime.today().year / 100) * 100 + int(m.group(0))
        else:
            return int(m.group(0))
    else:
        return None


def parse_datetime(msg):
    if msg is None or len(msg) == 0:
        return None
    #try:
    # dt = parse(msg, fuzzy=True)
    # return dt.strftime('%Y-%m-%d %H:%M:%S') #这段代码 原作者 减少的不好 这段代码可能会出现 年份问题,不如直接使用正则
    # except Exception as e:
    m = re.match(
        r"([0-9零一二两三四五六七八九十]+年)?([0-9一二两三四五六七八九十]+月)?"
        r"([0-9一二两三四五六七八九十]+[号日])?([上中下午晚早]+)?"
        r"([0-9零一二两三四五六七八九十百]+[点:\.时])?"
        r"([0-9零一二三四五六七八九十百]+分?)?([0-9零一二三四五六七八九十百]+秒)?",
        msg)
    if m.group(0) is not None: #,添加了一个try
        try:
            res = {
                "year": m.group(1),
                "month": m.group(2),
                "day": m.group(3),
                "hour": m.group(5) if m.group(5) is not None else '12',
                "minute": m.group(6) if m.group(6) is not None else '00',
                "second": m.group(7) if m.group(7) is not None else '00',
            }
            params = {}

            for name in res:
                if res[name] is not None and len(res[name]) != 0:
                    tmp = None
                    if name == 'year':
                        tmp = year2dig(res[name][:-1])
                    else:
                        tmp = cn2dig(res[name][:-1])
                    if tmp is not None:
                        params[name] = int(tmp)
            target_date = datetime.today().replace(**params)
            is_pm = m.group(4)
            if is_pm is not None:
                if is_pm == u'下午' or is_pm == u'晚上' or is_pm == u'中午':
                    hour = target_date.time().hour
                    if hour < 12:
                        target_date = target_date.replace(hour=hour + 12)
            if target_date < datetime.today() and datetime.today().hour - target_date.hour <= 12:
                target_date = target_date.replace(hour=target_date.time().hour + 12)
            return target_date
        except:
            return None
    else:
        return None


def check_time_valid(word):
    m = re.match("\d+$", word)
    if m:
        if len(word) <= 6:
            return None
        if int(word) > 30000000:
            return None
    word1 = re.sub('[号|日]\d+$', '日', word)
    if word1 != word:
        return check_time_valid(word1)
    else:
        return word1


# 时间提取
def time_extract(text):
    time_res = [] # 提取出来的时间字符串放到这里
    word = ''
    keyDate = {'今天': 0, '明天': 1, '后天': 2, '下周': 7}
    for k, v in psg.cut(text):
        if k == "个" or v=="eng":
            continue
    #   print(k,v)
        if k in keyDate:
            if word != '':
                time_res.append(word)
            word = (datetime.today() + timedelta(days=keyDate.get(k, 0))).strftime('%Y{y}%m{m}%d{d}').format(y='年',m='月',d='日')
        elif word != '':
            if v in ['m', 't']:
                word = word + k
            else:
                time_res.append(word)
                word = ''
        elif v in ['m', 't']:
            word = k
    if word != '':
        time_res.append(word)
    result = list(filter(lambda x: x is not None, [check_time_valid(w) for w in time_res]))
    print(result)
    final_res = [parse_datetime(w) for w in result]
#    print(final_res)
    return [x for x in final_res if x is not None]


text1 = '希望明天大家做好整理;做好地面清洁;去除异味;收好违章电器;在八点半之前离开宿舍(不管有无课程都离开宿舍);'
text2 = '这是不忘初心,牢记使命主题教育活动,建议同学踊跃报名,人数不设上限,8点前报给班长,特别是思想上积极要求进步的同学'
text3 = '明天上午10点左右校领导参观书院,请全体同学做好迎接检查准备:打扫2个宿舍卫生,清理杂物垃圾,杜绝违章电器,靠近过道窗帘拉开。特别是洗脸池,要彻底打扫。'
print(text3,time_extract(text3), sep=';')

编译原理一些基本知识

这是编译器的一个工作流程,分为词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成。而我们要提取时间的工作,用到了前三项即词法分析、语法分析、语义分析。如:图1
图1

词法分析将我们输入的自然语言转化为记号流。

在这里插入图片描述

语法分析则是对记号流进行句子的结构处理,生成语法树。
在这里插入图片描述
而语义分析则是要对语法进行‘任务’的先后排列,即可以看到任务运行的先后顺序。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

z137238796

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值