1. 程式人生 > >200行程式碼,一行行教你自制微信機器人

200行程式碼,一行行教你自制微信機器人

640?wx_fmt=jpeg

參加 2018 AI開發者大會,請點選 ↑


作者|上海小胖,四大諮詢的TechLead,mongoDB Professional 獲得者。「Python專欄」專注Python領域的各種技術:爬蟲、DevOps、人工智慧、Web開發等。還有「大航海計劃」,各種內推活動。


1

當初決定自己寫這麼個機器人有幾個原因:

1) 用一個windows客戶端工具運營公眾號,真的很侷限。雖然工具的功能很強大,能自動新增好友,自動拉好友入群,關鍵字回覆等等,但是有一個繞不開的點,它是一款客戶端工具,一款exe軟體。

2) 我是Mac,為了用這個工具,就要開著虛擬機器去操作。

3) 為了能一直自動新增好友,邀請入群,自動回覆等一系列操作,電腦就不能合上。

4) 在外面突然想到一個點,想操作群發了,GG,無能為力。

5) 其他……


640?wx_fmt=jpeg


2


基於以上的原因,就想著自己來一套算了。畢竟可以定製化的話,之後想要什麼就很方便了,而且在伺服器端掛個python服務要比開個windows 就為了掛一個exe要很多。


那麼首先需要確定需求,wxRobot我是準備長期維護、迭代的,所以顯然不可能像網上那些個指令碼一樣,一個檔案打通關。

另外功能自定義,就需要有版本引入,先做什麼,痛點是什麼都需要明確。我說下自己的選擇:


1) 痛點是不能自主化的管理公眾號、微訊號。

2) 最急迫解決的是之前exe工具用到的功能,畢竟這也是我用這個工具的原因。那麼有哪些功能呢?


  1. 群發訊息

  2. 自動新增好友

  3. 邀請好友入群

  4. 關鍵字回覆


3


既然是個專案,那麼該有的元件一個不能少,看一下我的目錄結構,這也是我自己總結出的common structure,大家可以參考一下,如果有好的建議歡迎大佬不吝留言。



640?wx_fmt=jpeg


  • app:專案業務模組。如果有多個模組就新增子目錄,例如:一個網站下的部落格模組、投票模組等。

  • core:核心元件。例如:資料庫元件、類-檔案元件等。

  • doc:文件。存放所有的文件,一般我會有固定的幾個:CHANGELOG.md、BUGLIST.md、TODOLIST.md。

  • etc:配置檔案。可以細分基本配置、業務配置等。

  • static:靜態檔案。

  • test:單元測試。

  • tmp:不需要進入版本控制的東西。

  • utility:輔助元件。和core相輔。


4


我把業務分為兩塊,filehelper算一個,好友相關的算一個。


好友相關的好理解,諸如新增好友、自動回覆、邀請入群等。filehelper是什麼呢?說白了,我們除了簡單的自動回覆、新增外,一定還希望做的更多吧?比如互動式指令。那這個filehelper就承擔了指令收發的角色。

所有的業務模組都基於一個BaseHandle,這樣底層的一些單元我就可以統一管控了:

色。



  

class BaseHandle:
   def __init__(self):
       '''
       self._meta = {
           'obj':{ # 訊息傳送物件
               'ul': [], # unlimit group
               'l': [], # limit group
               'r': [] # restrict
           },
           'reply':{
               'text': '',
               'article': '',
           }
       }
       '''

       self._usage = ''
       self._meta = {}
       self.current_cmd = None

   @property
   def usage(self):
       return self._usage

   @property
   def meta(self):
       return self._meta


再來看看FileHandle這個類,這也是當前版本最豐富的模組。這裡面有兩端邏輯:1.自動更新群組資訊。 2.註冊群發相關命令。


自動更新群組資訊的目的是因為itchat模組會將所有聯絡人以及群組資訊儲存在本地的一個pkl檔案中(pickle縮寫?),如果想提升群發訊息前獲取群組列表的速度,那麼就應該把資料放在記憶體裡(反正也沒多少資料),以下我把主要邏輯都羅列出來了,具體的程式碼太長了,暫時就不放出來了:


class FileHelper(BaseHandle):
   _usage = '''
   '''


   def __init__(self):
       super().__init__()
       self._meta = {
           ...
       }
       self._th_update = threading.Thread(target=self._update_meta, args=(), daemon=True)
       self.auto_update_groups()

   def auto_update_groups(self):
       # 自動更新群組
       self._th_update.start()

   def _update_meta(self):
       '''
       初始化限時推送的群組
       '''


       def _filter_restrict_groups(group):
           # 篩選出不能群發的群組

       def _filter_limit_groups(group):
           # 篩選出有時間限制的群組

       def _filter_unlimit_group(groups, limit_groups):
           # 篩選出不受限制的群組

       while True:
           time.sleep(30)
           # 更新群組資訊



註冊群發相關命令的思路就是做一個命令註冊器,因為群發訊息、文章、圖片等行為類似,針對不同的使用者群組傳送不同的訊息體。


所以我就把註冊器的成員分成了:型別(文字、圖片),物件(時間限制群組、無限制群組),行為(群發、單發)。


被裝飾器註冊的函式就成為了某個具有單獨意義的指令了。



  

class FileHelper(BaseHandle):
   ...

   def update_cmd(self, cmd):
       # 更新命令,用於動態註冊函式

   def _register_mass(func):
       @functools.wraps(func)
       def decorator(self, msg, *args, **kwargs):
           _action, _reply, _obj = func.__name__.split('_')
           if self._meta['action'][_action]:
               _to_user = self._meta['obj'][_obj]
               for _group in _to_user:
                   instance.send_msg(msg, _group['UserName'])
                   time.sleep(random.randrange(020))
               self._meta['action'][_action] = False
               self._current_cmd = None
               instance.send_msg('群發訊息傳送完畢', self._meta['extra']['UserName'])

       return decorator

   @_register_mass
   def mass_text_ul(self, msg=None):
       pass

   @_register_mass
   def mass_text_l(self, msg):
       pass

   @_register_mass
   def mass_text_test(self, msg):
       pass

   @_register_mass
   def mass_article_ul(self, msg):
       pass

   @_register_mass
   def mass_article_l(self, msg):
       pass


對比著效果圖來看看:


640?wx_fmt=jpeg


5


接下來就是新增好友部分了,目前只支援自動接受好友,根據打招呼自動設定備註,關鍵字回覆。



  

class Friend(BaseHandle):
   _usage = '''
   '''


   def __init__(self):
       super().__init__()
       self._meta = {
           ...
       }

   def is_biz(self, msg):
        
# 判斷是不是商務合作


看下效果圖:


640?wx_fmt=jpeg

6

講完核心程式碼後,再來講下中間經歷的幾個看不到的版本吧。


最一開始就是實現功能咯,沒想很多,但是發現程式碼重複太多了,邏輯都差不多,一堆程式碼太醜了。優化後的程式碼就是第一版中的群發註冊器函式。


接著原本的BaseHandle基類太重了,想的很好,把itchat方法都重寫在基類裡,這樣就不用在其他地方呼叫itchat例項了,但是結果就是所有的子類都可以做同樣的動作,就變成了filehelper.send_msg(), friend.send_image()了,這樣對於同一個方法就會產生歧義了。因此就把基類裡所有重寫itchat方法的函式都去了,就保留了業務程式碼,並分別移到對應的類裡去,而原本itchat的方法還是用itchat例項去操作。


接著關於itchat例項、FileHelper例項、Friend例項等的共享問題,容易造成重疊,重複使用、互相引用問題。解決辦法目前就是把itchat例項單獨在配置檔案裡初始化了,這也同時解決了上一個問題,其他業務類的例項採用單例模式,在類外面暴露一個統一的例項。


7


好了,這回是真花了功夫把這套程式碼講完了,雖然還是相對簡陋了,但迫於時間關係,先發出來了。之後會繼續優化、健碩它。


今天也和一位大佬討論了下這個專案,有很多值得思考的地方。


640?wx_fmt=jpeg


如果你對這份程式碼也感興趣的話,歡迎底部留言~


--【完】--


2018 AI開發者大會


AI技術年度盛會即將開啟!11月8-9日,來自Google、Amazon、微軟、Facebook、LinkedIn、阿里巴巴、百度、騰訊、美團、京東、小米、位元組跳動、滴滴、商湯、曠視、思必馳、第四正規化、雲知聲等企業的技術大咖將帶來工業界AI應用的最新思維。


如果你是某個AI技術領域的專業人才,或想尋求將AI技術整合至傳統企業業務當中,掃碼填寫大會註冊資訊表,我們將從中挑選出20名相關性最高的幸運讀者,送出單場分論壇入場券。大會嘉賓陣容和議題,請檢視文末海報。


640?wx_fmt=png


此外,如果你想與所有參會大牛充分交流溝通,點選閱讀原文購票,使用優惠碼:AI2018-DBY 購買兩日通票,立減999元;此外大會還推出了1024定製票,主會+分會自由組合,精彩隨心。


640?wx_fmt=jpeg


推薦閱讀

脣語識別技術的開源教程,聽不見聲音我也能知道你說什麼!

首發|機器學習未來十年:你需要把握的趨勢和熱點

全面梳理百度世界大會,李彥巨集又新吹了幾個牛!

她說:真的,沒事別嫁程式設計師

中心化交易所弊端盡顯,DEX時代即將到來?使用者分析告訴你

2019秋招AI崗位競爭究竟有多激烈?

學習這麼多演算法到底在解決哪些問題?深度學習之外,我們要選擇誰?


點選閱讀原文,檢視大會更多詳情。2018 AI開發者大會——擺脫焦慮,擁抱技術前沿。