DIY 微信群聊機(jī)器人

前言

這幾天我的一個(gè)小伙伴問我能不能給 Ta 做一個(gè)配置靈活的微信群聊天機(jī)器人,之前了解過 itchat 庫的使用聚霜,我就爽快的答應(yīng)了,花了一個(gè)晚上珠叔,終于做出了雛形蝎宇。

電腦上運(yùn)行程序如下:

7.gif

手機(jī)上的信息如下:

1.jpg

其實(shí)基于 itchat 的微信機(jī)器人早已經(jīng)爛大街了,但大多數(shù)過于簡(jiǎn)單祷安,相比較而言姥芥,我的這個(gè)程序有下面幾大鮮明的特色:

1、支持打開/關(guān)閉指定群聊的自動(dòng)回復(fù)汇鞭,只需用記事本打開 group.csv 文件凉唐,填寫想要打開自動(dòng)回復(fù)群聊名稱即可,每次輸完一個(gè)群聊名稱霍骄,必須換行台囱。

2、支持自定義設(shè)置關(guān)鍵詞回復(fù)读整,只需用記事本打開 keyword.csv 文件簿训,按照{關(guān)鍵詞,回復(fù)}的格式添加即可,而不需要在代碼中做任何修改。輸完一個(gè)鍵值對(duì)强品,同樣也需要換行膘侮,注意輸入的是英文逗號(hào)。

3的榛、支持定時(shí)群發(fā)消息琼了,而且時(shí)間、消息可以在程序運(yùn)行中動(dòng)態(tài)修改困曙。

4表伦、有較為良好的 GUI 界面,其中色彩搭配參考了微信的簡(jiǎn)約設(shè)計(jì)慷丽。

DIY 玩法

1、面向商戶

作為商戶鳄哭,維護(hù)群的時(shí)候可能有這樣的需求要糊,定時(shí)發(fā)送消息。比如回復(fù) xxx 可獲得 yyy,同時(shí)在keyword.csv 文件中事先寫好妆丘。這樣锄俄,可以讓群成員各取所需,你又不需要打字勺拣、復(fù)制粘貼奶赠,還可以同時(shí)處理多個(gè)群,省心還高效药有。

2毅戈、面向普通個(gè)人

定時(shí)向男女朋友,父母親人發(fā)送晚安祝福消息等愤惰。

3苇经、部署至阿里云服務(wù)器

有個(gè)缺陷就是如果想一直自動(dòng)群發(fā)消息的話,你的電腦就必須一直開著宦言,但是部署至云服務(wù)器可以解決這個(gè)問題扇单。部署流程可以參考我之前的文章, 自己動(dòng)手打造mini型QQ(二):從局域網(wǎng)到互聯(lián)網(wǎng)的miniQQ 同時(shí)給出阿里云服務(wù)器優(yōu)惠購買傳送門。點(diǎn)擊傳送

代碼的詳細(xì)設(shè)計(jì)

1奠旺、代碼的架構(gòu)

由于引入了 GUI蜘澜,GUI代碼塊和負(fù)責(zé)群發(fā)消息的代碼塊一樣,都是阻塞型的响疚,為此鄙信,程序就必須引入多線程機(jī)制,其中 GUI 界面是主線程稽寒,負(fù)責(zé)群發(fā)消息的代碼塊運(yùn)行在子線程扮碧,線程間的通信我用的是 wxPython 內(nèi)置的 wx.lib.pubsub 模塊,一旦子線程執(zhí)行了相應(yīng)的動(dòng)作,就通過 wx.CallAfter(pub.sendMessage) 接口發(fā)送消息給通知 GUI 線程慎王,從而保證 GUI 能夠及時(shí)刷新并不至于卡頓蚓土。

2.png

2、代碼的流程

首先是加載相應(yīng)的配置文件赖淤,確定要開啟哪些群聊的自動(dòng)回復(fù)蜀漆,以及關(guān)鍵詞回復(fù)信息。也正因如此咱旱,在程序執(zhí)行過程中确丢,這些信息是不能被動(dòng)態(tài)改變的。

其中加載 keyword的代碼如下:

def load_keyword(self):
    global keywords
    with open('keyword.csv', 'r', encoding='utf-8', newline='') as f:
        reader = csv.reader(f)
        for i, line in enumerate(reader):
            if i == 0:
                continue
            keywords[line[0]] = line[1]

keywords 設(shè)置為全局變量方便后面使用吐限,避免傳參調(diào)用鲜侥,判斷 i == 0 是為了去掉 csv 文件的第一行頭部信息。

負(fù)責(zé)群發(fā)的主要代碼塊如下诸典,代碼注釋較為清晰描函,不再贅述

@itchat.msg_register(TEXT, isGroupChat=True)
def group_text(msg):
    global keywords
    groups = itchat.get_chatrooms(update=True)
    for group in groups:
        # 群的 NickName 是群名稱,UserName 是群id(以兩個(gè)@開始)
        # Python/Java 學(xué)習(xí)交流群
        if group['NickName'] in group_names:  # 從群中找到指定的群聊
            group_id = msg['FromUserName']
            # 防止其他群消息的的干擾
            if not group_id == group['UserName']:
                break
            # 準(zhǔn)備回復(fù)的消息
            keys = keywords.keys()
            key = ''
            for i in keys:
                if i in msg['Text']:
                    key = i
                    break
            if key == '':
                return
            message = keywords.get(key)
            # 在消息中找到 發(fā)送人的id
            sender_id = msg['ActualUserName']
            # 有時(shí) group['MemberList'] 為空狐粱,改變思路由群 id 獲取群聊成員
            # group_info = itchat.update_chatroom(msg['ToUserName'], detailedMember=True)
            # if len(group_info) == 0:
            # toUserName 是自己在群聊發(fā)消息時(shí)舀寓,群 id 在消息里的 key
            # FromUserName 是別人在群里發(fā)時(shí),群 id 在消息里的 key
            group_info = itchat.update_chatroom(group_id, detailedMember=True)
            memberlist = group_info['MemberList']
            for member in memberlist:
                # 找到消息的發(fā)送者
                if member['UserName'] == sender_id:
                    # 如果有備注名肌蜻,群聊顯示的是備注名
                    to_user = member['RemarkName']
                    if len(to_user) == 0:
                        # 否則顯示成員自己修改的在群里的昵稱
                        to_user = member['DisplayName']
                    if len(to_user) == 0:
                        # 否則顯示他微信號(hào)的昵稱
                        to_user = member['NickName']
                    itchat.send_msg('@{}\n{}'.format(to_user, message), group['UserName'])
                    wx.CallAfter(pub.sendMessage, "update", msg="回復(fù)群聊[{}]成員[{}]成功:[{}]".format(group['NickName'],to_user,message))

負(fù)責(zé)定時(shí)群發(fā)的代碼和上面的代碼比較獨(dú)立互墓,在子線程開始的同時(shí),開始執(zhí)行定時(shí)群發(fā)的邏輯

def run(self):
    global t
    t = threading.Timer(minutes * 60, self.auto_timer)
    t.start()
    self.load_keyword()
    self.load_group()
    itchat.auto_login(hotReload=True)
    itchat.run()

其中主要的函數(shù)是 threading.Timer(minutes * 60, self.auto_timer),它的意思是在負(fù)責(zé)執(zhí)行群發(fā)的線程里蒋搜,再開一個(gè)線程篡撵,這個(gè)線程間隔minutes * 60 秒后去執(zhí)行回調(diào)函數(shù) self.auto_timer,但是這樣只能觸發(fā)一次,沒辦法一直輪詢齿诞,解決辦法是在回調(diào)函數(shù)里面再去執(zhí)行 threading.Timer(minutes * 60, self.auto_timer),有點(diǎn)兒類似于遞歸調(diào)用酸休,和遞歸不同的是,調(diào)用是沒有終止條件的祷杈,但并不會(huì)產(chǎn)生內(nèi)存溢出斑司,因?yàn)槎〞r(shí)器的存在,時(shí)間一到一觸發(fā)回調(diào)函數(shù)但汞,這個(gè)線程的生命就到此為止了宿刮,因此在整個(gè)程序運(yùn)行期間,活躍線程的數(shù)目?jī)H僅只是個(gè)位數(shù):

def auto_timer(self):
    global auto_message
    groups = itchat.get_chatrooms(update=True)
    for group in groups:
        if group['NickName'] in group_names:
            itchat.send_msg('{}'.format(auto_message), group['UserName'])
            wx.CallAfter(pub.sendMessage, "update",
                         msg="群聊[{}]定時(shí)消息:[{}]發(fā)送成功".format(group['NickName'], auto_message))

    global t  # 把 t 設(shè)置為全局變量
    t = threading.Timer(minutes * 60, self.auto_timer)
    t.start()

GUI 部分的代碼由于篇幅限制,就不貼出來了私蕾。

如何體驗(yàn)

關(guān)注公眾號(hào)月小水長(zhǎng),后臺(tái)回復(fù) 微信群機(jī)器人 即可獲得,不用寫一行代碼僵缺,做任何配置,雙擊運(yùn)行即可體驗(yàn)群聊自動(dòng)回復(fù)踩叭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末磕潮,一起剝皮案震驚了整個(gè)濱河市翠胰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌自脯,老刑警劉巖之景,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異膏潮,居然都是意外死亡锻狗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門焕参,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轻纪,“玉大人,你說我怎么就攤上這事叠纷】讨悖” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵讲岁,是天一觀的道長(zhǎng)我擂。 經(jīng)常有香客問我,道長(zhǎng)缓艳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任看峻,我火速辦了婚禮阶淘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘互妓。我一直安慰自己溪窒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布冯勉。 她就那樣靜靜地躺著澈蚌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灼狰。 梳的紋絲不亂的頭發(fā)上宛瞄,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音交胚,去河邊找鬼份汗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蝴簇,可吹牛的內(nèi)容都是我干的杯活。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼熬词,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼旁钧!你這毒婦竟也來了吸重?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤歪今,失蹤者是張志新(化名)和其女友劉穎嚎幸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彤委,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鞭铆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了焦影。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片车遂。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖斯辰,靈堂內(nèi)的尸體忽然破棺而出舶担,到底是詐尸還是另有隱情,我是刑警寧澤彬呻,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布衣陶,位于F島的核電站,受9級(jí)特大地震影響闸氮,放射性物質(zhì)發(fā)生泄漏剪况。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一蒲跨、第九天 我趴在偏房一處隱蔽的房頂上張望译断。 院中可真熱鬧,春花似錦或悲、人聲如沸孙咪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翎蹈。三九已至,卻和暖如春男公,著一層夾襖步出監(jiān)牢的瞬間荤堪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工理澎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逞力,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓糠爬,卻偏偏與公主長(zhǎng)得像寇荧,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子执隧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353