├── .gitignore ├── .idea ├── .gitignore ├── AntifraudBOT.iml ├── compiler.xml ├── jarRepositories.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.md ├── __init__.py ├── crawler ├── README.MD ├── database │ └── sql_executor.py ├── test.py ├── utils │ ├── add_database.py │ ├── auto_utils.py │ ├── downloader_manager.py │ ├── findauthor.py │ ├── get_reply.py │ ├── get_video_html.py │ ├── logger.py │ ├── requester.py │ └── seg_downloader.py └── worker.py ├── dialog.py ├── ding-dong-bot.py ├── inspurai.py ├── log ├── 11_11_2022.txt ├── 12_10_2022.txt ├── 15_11_2022.txt ├── 16_11_2022.txt └── 30_09_2022.txt ├── memory ├── __init__.py ├── forget.py └── longshort.py ├── prompt ├── bot_example.txt ├── bot_info.txt ├── scripts └── users.json ├── python-wechaty ├── .codecov.yml ├── .editorconfig ├── .gitignore ├── .pre-commit-config.yaml ├── .pylintrc ├── .readthedocs.yaml ├── .travis.yml ├── LICENSE ├── MAINTAINERS ├── MANIFEST.in ├── Makefile ├── NOTICE ├── README.md ├── VERSION ├── docs │ ├── api │ │ ├── accessory.md │ │ ├── config.md │ │ ├── plugin.md │ │ ├── types.md │ │ ├── user │ │ │ ├── contact.md │ │ │ ├── contact_self.md │ │ │ ├── favorite.md │ │ │ ├── friendship.md │ │ │ ├── image.md │ │ │ ├── message.md │ │ │ ├── mini_program.md │ │ │ ├── room.md │ │ │ ├── room_invitation.md │ │ │ ├── tag.md │ │ │ └── url_link.md │ │ ├── utils │ │ │ ├── async_helper.md │ │ │ ├── date_util.md │ │ │ ├── link.md │ │ │ ├── qr_code.md │ │ │ ├── qrcode_terminal.md │ │ │ └── type_check.md │ │ └── wechaty.md │ ├── explanation │ │ ├── different_protocol.md │ │ ├── index.md │ │ └── why_plugin.md │ ├── faq │ │ ├── common.md │ │ ├── faq.md │ │ └── what-is-a-puppet.md │ ├── how-to-contribute-for-docs.md │ ├── how-to │ │ ├── how-to_add_friendship.md │ │ ├── how-to_auto_reply.md │ │ ├── how-to_finder.md │ │ ├── how-to_github_webhook.md │ │ ├── how-to_gitlab_webhook.md │ │ ├── how-to_introduction.md │ │ ├── how-to_message_forward.md │ │ ├── how-to_rasa.md │ │ ├── how-to_room_inviter.md │ │ ├── how-to_scheduler.md │ │ ├── how-to_use_plugin.md │ │ ├── use-padlocal-protocol.md │ │ └── use-web-protocol.md │ ├── img │ │ ├── favicon.ico │ │ ├── getting-started │ │ │ └── python-wechaty.png │ │ ├── introduction │ │ │ └── cloud.png │ │ ├── wechaty-icon-white.svg │ │ └── wechaty-logo.svg │ ├── index.md │ ├── introduction.md │ ├── introduction │ │ ├── index.md │ │ ├── use-padlocal-protocol.md │ │ ├── use-paimon-protocol.md │ │ └── use-web-protocol.md │ ├── references │ │ ├── contact-self.md │ │ ├── contact.md │ │ ├── filebox.md │ │ ├── friendship.md │ │ ├── index.md │ │ ├── message.md │ │ ├── room-invitation.md │ │ ├── room.md │ │ └── wechaty.md │ └── tutorials │ │ ├── getting-started.md │ │ ├── index.md │ │ ├── use_padlocal_getting_started.md │ │ ├── use_paimon_getting_started.md │ │ ├── use_web_getting_started.md │ │ └── videos.md ├── examples │ ├── contact-bot.py │ ├── ding-dong-bot-oop.py │ ├── ding-dong-bot.py │ ├── health_check_plugin.py │ └── plugin-server-bot.py ├── mkdocs.yml ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── scripts │ └── check_python_version.py ├── setup.cfg ├── setup.py ├── src │ └── wechaty │ │ ├── __init__.py │ │ ├── accessory.py │ │ ├── config.py │ │ ├── exceptions.py │ │ ├── plugin.py │ │ ├── py.typed │ │ ├── types.py │ │ ├── user │ │ ├── __init__.py │ │ ├── contact.py │ │ ├── contact_self.py │ │ ├── favorite.py │ │ ├── friendship.py │ │ ├── image.py │ │ ├── message.py │ │ ├── message.pyi │ │ ├── mini_program.py │ │ ├── room.py │ │ ├── room.pyi │ │ ├── room_invitation.py │ │ ├── tag.py │ │ └── url_link.py │ │ ├── utils │ │ ├── __init__.py │ │ ├── async_helper.py │ │ ├── date_util.py │ │ ├── link.py │ │ ├── qr_code.py │ │ ├── qrcode_terminal.py │ │ └── type_check.py │ │ ├── version.py │ │ └── wechaty.py ├── tests │ ├── accessory_test.py │ ├── config_test.py │ ├── conftest.py │ ├── room_test.py │ ├── smoke_testing_test.py │ ├── timestamp_test.py │ ├── url_link_test.py │ ├── user_message_test.py │ ├── utils_test.py │ ├── version_test.py │ └── wechaty_test.py └── wip │ └── wechaty │ └── __init__.py ├── rasa_chatbot_cn ├── .gitignore ├── Makefile ├── __init__.py ├── actions │ └── actions.py ├── config.yml ├── configs │ └── endpoints.yml ├── data │ ├── core │ │ └── stories.md │ └── nlu │ │ └── nlu.json ├── dev │ ├── Dockerfile.dev │ ├── deploy_dev.sh │ └── run.sh ├── domain.yml └── process.py ├── rooms.json ├── script ├── 源1.0预训练语言模型使用示例V1.pdf └── 源APIExp使用手册0510.pdf ├── test.py └── url_config.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/AntifraudBOT.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | sys.path.append(os.path.abspath(os.path.dirname(__file__))) -------------------------------------------------------------------------------- /crawler/README.MD: -------------------------------------------------------------------------------- 1 | Green类可以爬取一个up的所有动态信息、相册、收藏夹、视频索引、关注与被关注信息,并添加到指定的数据库中,再使用该类中的run方法,便可以遍历爬取所有视频本体和附加信息; 2 | Blue类是一个仅用于不断爬取用户数据和用户关系的类,可以爬取每个用户的动态信息、相册、收藏夹、视频索引信息,并能够在关注列表中发现新的用户,进而建立起一个庞大的用户信息和用户关系网络,可以制作数据集、推荐系统以及搞科研用。 3 | 4 | 5 | ## 准备工作 6 | ### pip依赖库 7 | requests 8 | tqdm 9 | ### 系统依赖插件 10 | ffmpeg (需添加至环境变量,macos、linux使用brew、apt、yum等工具均可直接安装,windows下安装方法自行百度) 11 | 12 | 13 | ## 使用方法 14 | 这里只讲强大的Green类 15 | 16 | 看到worker.py,实例化一个Green类,传入数据库路径(没有数据库的话会自动创建)、up的视频路径、up的相册的路径(没有皆会自动创建),即可初始化完成。 17 | 18 | 调用find_ups([up主mid, ])即可获取一个up主的态、相册、收藏夹、视频索引、关注与被关注信息,并添加到sqlite数据库中。 19 | 20 | 在调用run()方法,即可按照视频索引信息按顺序爬取up主的视频与附加信息到指定目录中。 21 | 22 | 详细用例可以在文件中的main函数中找到。 23 | 24 | Enjoy it~ 25 | 26 | 27 | 28 | ## TODO 29 | 当系统封禁ip时自动切换内网ip地址。 30 | 31 | 32 | -------------------------------------------------------------------------------- /crawler/database/sql_executor.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | class SQLHandler(object): 4 | def __init__(self, path): 5 | self.db = sqlite3.connect(path) 6 | self.cursor = self.db.cursor() 7 | 8 | def execute(self, sql): 9 | try: 10 | # print(sql) 11 | self.cursor.execute(sql) 12 | self.db.commit() 13 | except sqlite3.IntegrityError: 14 | pass 15 | 16 | def execute_return(self, sql): 17 | ret = self.cursor.execute(sql) 18 | return ret 19 | 20 | def insert_binary(self, sql, bin_obj, ): 21 | # print(sql) 22 | self.cursor.execute(sql, (bin_obj, )) 23 | self.db.commit() 24 | 25 | def close(self): 26 | self.db.close() -------------------------------------------------------------------------------- /crawler/test.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | from decimal import Decimal 4 | import pprint 5 | headers ={ 6 | 'referer': 'https://www.bilibili.com/video/BV1t3411p7Vq?spm_id_from=444.41.0.0', 7 | 'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53', 8 | } 9 | main_url = 'https://api.bilibili.com/x/v2/reply/main?' 10 | main_params = { 11 | 'jsonp': 'jsonp', 12 | 'next': 1, 13 | 'type': '1', 14 | 'oid': '425009881', 15 | 'mode': '3', 16 | 'plat': '1', 17 | '_': '1648289436898' 18 | } 19 | reply_url = 'https://api.bilibili.com/x/v2/reply/reply' 20 | reply_params = { 21 | 'jsonp': 'jsonp', 22 | 'pn': '1', 23 | 'type': '1', 24 | 'oid': '425009881', 25 | 'ps': '100', 26 | 'root': '106841868432', 27 | '_': '1648290346968' 28 | } 29 | proxies={ 30 | 'http':'20.24.65.59:6655' 31 | } 32 | dic = {} 33 | dic['count'] = Decimal('0') 34 | #楼中楼 35 | def parse_reply(root,ps,count): 36 | reply_params['root'] = root 37 | reply_params['ps'] = ps 38 | response = requests.get(url=reply_url, headers=headers, params=reply_params) 39 | page_text_json = response.json() 40 | for comment in page_text_json['data']['replies']: 41 | dic['count'] += Decimal('0.0001') 42 | dic['username'] = comment['member']['uname'] 43 | dic['content'] = comment['content']['message'] 44 | dic['like_count'] = comment['like'] 45 | print(dic) 46 | f.write(str(dic['count']) + str(dic['username']) + str(dic['content']) + str(dic['like_count']) + '\n') 47 | dic['count'] = count 48 | #评论楼层 49 | def parse_main_reply(): 50 | response = requests.get(url = main_url, headers = headers,params = main_params) 51 | page_text = response.text 52 | page_text_json = response.json() 53 | with open('test.html','w',encoding='utf-8') as fp: 54 | fp.write(page_text) 55 | fp.close() 56 | if page_text_json['data']['cursor']['is_end'] == False: 57 | for comment in page_text_json['data']['replies']: 58 | dic['count'] += Decimal('1') 59 | dic['username'] = comment['member']['uname'] 60 | dic['content'] = comment['content']['message'] 61 | dic['reply_count'] = comment['rcount'] 62 | dic['reply_id'] = comment['rpid'] 63 | dic['like_count'] = comment['like'] 64 | dic['total_reply_num'] = page_text_json['data']['cursor']['all_count'] 65 | dic['is_end'] = page_text_json['data']['cursor']['is_end'] 66 | print(dic) 67 | f.write(str(dic['count'])+str(dic['username'])+str(dic['content'])+str(dic['reply_count'])+str(dic['reply_id'])+str(dic['like_count'])+str(dic['total_reply_num'])+str(dic['is_end'])+'\n') 68 | if dic['reply_count'] != 0: 69 | parse_reply(dic['reply_id'],dic['reply_count'],dic['count']) 70 | main_params['next'] += 1 71 | return parse_main_reply() 72 | elif page_text_json['data']['cursor']['is_end'] == True: 73 | print('结束') 74 | f = open('test.txt','a',encoding='utf-8') 75 | parse_main_reply() 76 | f.close() -------------------------------------------------------------------------------- /crawler/utils/auto_utils.py: -------------------------------------------------------------------------------- 1 | from utils.add_database import DBProxy 2 | 3 | class AutoUtils: 4 | def __init__(self, db_path): 5 | self.db_path = db_path 6 | self.db_proxy = DBProxy(db_path) 7 | def get_one_user(self): 8 | ret = self.db_proxy.select_one_author() 9 | return ret[0] 10 | 11 | def flag_one_video(self, bvid): 12 | self.db_proxy.flag_one_video(bvid) 13 | pass 14 | 15 | def get_one_vid(self,): 16 | return self.db_proxy.select_one_video()[0] 17 | 18 | def flag_one_err_author(self, mid): 19 | return self.db_proxy.flag_one_author_error(mid) 20 | 21 | 22 | def flag_one_user_done(self, mid): 23 | self.db_proxy.flag_one_author(mid) 24 | 25 | if __name__ == '__main__': 26 | 27 | autoutils = AutoUtils('test2.db') 28 | # autoutils.get_one_user() 29 | print(autoutils.get_one_vid()) 30 | # TODO:增加断点续传功能 -------------------------------------------------------------------------------- /crawler/utils/get_reply.py: -------------------------------------------------------------------------------- 1 | # coding:utf8 2 | import requests 3 | 4 | 5 | def get_raw_reply(aid): 6 | headers = { 7 | 'accept': '*/*', 8 | # 'accept-encoding': 'gzip, deflate, br', 9 | 'accept-encoding': 'deflate', 10 | 'accept-language': 'zh-CN,zh;q=0.9', 11 | 'cache-control': 'no-cache', 12 | # 'cookie': 'pgv_pvi=300764160; rpdid=olwwwliplodosoqqwokww; _uuid=EA9827E7-550F-BFB8-CA1B-8257DCDC960468224infoc; buvid3=93A293B0-2EAC-4F96-87BB-E7D398CBCC57148797infoc; CURRENT_FNVAL=80; blackside_state=1; fingerprint=bc56ed3fa67484bb54a6f2c0d34d101f; buvid_fp=93A293B0-2EAC-4F96-87BB-E7D398CBCC57148797infoc; buvid_fp_plain=93A293B0-2EAC-4F96-87BB-E7D398CBCC57148797infoc; SESSDATA=d82262f7%2C1642232589%2Cee061%2A71; bili_jct=bfba5357d0ca1966160cce82a70380b7; DedeUserID=23064683; DedeUserID__ckMd5=864e26ed3b2a0940; sid=56yroqa6; CURRENT_QUALITY=80; PVID=1; bfe_id=5db70a86bd1cbe8a88817507134f7bb5', 13 | 'pragma': 'no-cache', 14 | # 'referer': 'https://www.bilibili.com/video/BV1XL411H79r', 15 | 'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"', 16 | 'sec-ch-ua-mobile': '?0', 17 | 'sec-fetch-dest': 'script', 18 | 'sec-fetch-mode': 'no-cors', 19 | 'sec-fetch-site': 'same-site', 20 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36' 21 | } 22 | page = 0 23 | reply_lst = [] 24 | while True: 25 | assert aid.isdigit(), 'aid is not digit' 26 | url = 'https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&next={}&type=1&oid={}&mode=3&plat=1&'.format(page, aid) 27 | # url = 'https://api.bilibili.com/x/v2/reply/main?callback=jQuery17207723215742327245_1627299231613&jsonp=jsonp&next=7&type=1&oid=461774991&mode=3&plat=1&_=1627299537026' 28 | req = requests.get(url, headers=headers) 29 | req.encoding = req.apparent_encoding 30 | # print(req.content.decode('utf8')) 31 | content = req.content.decode('utf8') 32 | json = content.replace('null', 'None').replace('true', 'True').replace('false', 'False') 33 | json = eval(json) 34 | # print(json) 35 | if json["data"]['cursor']['is_end']: 36 | break 37 | reply_lst.append(json) 38 | page += 1 39 | 40 | # print(reply_lst) 41 | return reply_lst 42 | 43 | 44 | if __name__ == '__main__': 45 | 46 | # print(get_raw_reply('461774991')) 47 | # print(get_raw_reply('461774991')) 48 | print(get_raw_reply('360863614')) 49 | 50 | -------------------------------------------------------------------------------- /crawler/utils/logger.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pandas as pd 3 | 4 | def logger(info, type='INFO'): 5 | print('[{}]{}: {}'.format(time.strftime("%Y-%m-%d %H:%M:%S"),type, info)) 6 | 7 | def err_logger(err_msg, _id, err_code=0, err_log_path='err.log'): 8 | """ 9 | 0: following err 10 | 1: danmaku err 11 | 2: follower err 12 | 3: 13 | 14 | :param err_msg: 15 | :param _id: 16 | :param status_code: 17 | :param err_log_path: 18 | :return: 19 | """ 20 | df = pd.DataFrame([[err_msg, _id, err_code], ]) 21 | df.to_csv(err_log_path, mode='a', header=None) 22 | 23 | 24 | if __name__ == '__main__': 25 | pass 26 | # logger('qweqwe') 27 | # err_logger('123', '34') -------------------------------------------------------------------------------- /crawler/utils/requester.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | def request(url, headers=None): 4 | if not headers: 5 | headers = {'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36'} 6 | return requests.get(url, headers=headers).content -------------------------------------------------------------------------------- /crawler/utils/seg_downloader.py: -------------------------------------------------------------------------------- 1 | from utils.requester import request 2 | def download_segment(url): 3 | headers = { 4 | 'accept': '*/*', 5 | 'accept-encoding': 'identity', 6 | 'accept-language': 'zh-CN,zh;q=0.9', 7 | 'if-range': 'Thu, 22 Jul 2021 09:43:08 GMT', 8 | 'origin': 'https://www.bilibili.com', 9 | 'range': 'bytes=0-1995353', 10 | 'referer': 'https://www.bilibili.com/video/BV1XL411H79r', 11 | 'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"', 12 | 'sec-ch-ua-mobile': '?0', 13 | 'sec-fetch-dest': 'empty', 14 | 'sec-fetch-mode': 'cors', 15 | 'sec-fetch-site': 'cross-site', 16 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36' 17 | } 18 | # url='https://xy121x31x142x181xy.mcdn.bilivideo.cn:4483/upgcxcode/56/25/374632556/374632556_nb2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1626969237&gen=playurlv2&os=mcdn&oi=1885373441&trid=000191f5b7d25fed4e63bbd80e017001bc64u&platform=pc&upsig=e74d310419272cfdb787edfcf3a9434c&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mcdnid=9000626&mid=23064683&bvc=vod&nettype=0&orderid=0,3&agrr=0&logo=A0000100' 19 | 20 | # url2 = 'https://xy171x39x15x196xy.mcdn.bilivideo.cn:4483/upgcxcode/56/25/374632556/374632556_nb2-1-30080.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1627038912&gen=playurlv2&os=mcdn&oi=1885389723&trid=0001d1522260ac414b16bf9a5541b8578eb0u&platform=pc&upsig=356be02417e4af1928ec3d264bb8f943&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mcdnid=9000723&mid=28697381&bvc=vod&nettype=0&orderid=0,3&agrr=1&logo=A000010' 21 | 22 | content = request(url, headers=headers) 23 | return content 24 | 25 | 26 | 27 | 28 | 29 | # quit() 30 | # with open('test.m4s', 'wb') as f: 31 | # f.write(c) 32 | # f.close() 33 | 34 | -------------------------------------------------------------------------------- /crawler/worker.py: -------------------------------------------------------------------------------- 1 | from utils.logger import logger, err_logger 2 | from utils.auto_utils import AutoUtils 3 | from utils.findauthor import run 4 | from utils.downloader_manager import executor, single_video 5 | 6 | import os 7 | import argparse 8 | from tqdm import tqdm 9 | 10 | class GreenSystem: 11 | __doc__ = """ 12 | 用于下载B站视频or评论,从命令行获取要被爬取的up(可以遍历也可以指定) 13 | 启动后,将不停地爬取数据库中的视频 14 | """ 15 | 16 | def __init__(self, db_path, user_dy_path, video_down_path): 17 | self.db_path = db_path 18 | self.user_dy_path = user_dy_path 19 | self.video_down_path = video_down_path 20 | self.check_dirs() 21 | self.findauthor = run 22 | self.auto_utils = AutoUtils(db_path=self.db_path) 23 | 24 | @staticmethod 25 | def make_dirs(path): 26 | if not os.path.exists(path): 27 | os.makedirs(path, exist_ok=True) 28 | 29 | def check_dirs(self): 30 | self.make_dirs(self.user_dy_path) 31 | self.make_dirs(self.video_down_path) 32 | 33 | def run(self): 34 | while True: 35 | bvid = self.auto_utils.get_one_vid() 36 | # executor([bvid,], location=self.video_down_path) 37 | single_video(bvid, location=self.video_down_path) 38 | self.auto_utils.flag_one_video(bvid) 39 | 40 | 41 | def find_ups(self, ups=[]): 42 | for mid in tqdm(ups): 43 | self.findauthor(mid, down_pics=True, down_loc=self.user_dy_path, db_path=self.db_path) 44 | 45 | 46 | class BlueSystem(object): 47 | __doc__ = """ 48 | 不获取视频和图片,专注于研究单个用户数据和用户关系 49 | """ 50 | 51 | def __init__(self, db_path): 52 | self.db_path = db_path 53 | self.findauthor = run 54 | self.auto_utils = AutoUtils(db_path=self.db_path) 55 | 56 | def get_seed(self, ups=[]): 57 | for mid in tqdm(ups): 58 | self.findauthor(mid, down_pics=False, db_path=self.db_path, down_loc=None, 59 | get_img=False, get_collect=False, get_update=True, vid_limit=20) 60 | 61 | def run(self): 62 | while True: 63 | mid = self.auto_utils.get_one_user() 64 | # try: 65 | self.findauthor(mid, down_pics=False, db_path=self.db_path, down_loc=None, get_img=False, get_collect=False, get_update=True, vid_limit=20) 66 | # except: 67 | # self.auto_utils.flag_one_err_author(mid) 68 | 69 | 70 | def main(): 71 | argument = argparse.ArgumentParser() 72 | argument.add_argument('-s', '--seed-mid', default='``NULL``', type=str, help="Target user's mid (required).") 73 | argument.add_argument('-d', '--db', default='GREEN.DB', type=str, help='Database path.') 74 | argument.add_argument('-ud', '--user-dynamics', default='data/green/user_dynamics', type=str, help='User dynamic photos path.') 75 | argument.add_argument('-vp', '--video-path', default='data/green/videos', type=str, help='User Videos path.') 76 | argument.add_argument('-r', '--run', action='store_true', help='Enable Endless Running Mode. ') 77 | 78 | args = argument.parse_args() 79 | 80 | green_system = GreenSystem(db_path=args.db, user_dy_path=args.user_dynamics, video_down_path=args.video_path) 81 | if args.seed_mid != '```NULL```': 82 | green_system.find_ups([args.mid, ]) 83 | if args.run: 84 | green_system.run() 85 | 86 | 87 | def main_blue(): 88 | blue_system = BlueSystem(db_path='BLUE.DB') 89 | # blue_system.get_seed(['13074237', ]) 90 | blue_system.run() 91 | 92 | 93 | if __name__ == '__main__': 94 | main_blue() -------------------------------------------------------------------------------- /ding-dong-bot.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | Authors: Huan LI (李卓桓) 4 | Jingjing WU (吴京京) 5 | 2020 @ Copyright Wechaty Contributors 6 | Licensed under the Apache License, Version 2.0 (the 'License'); 7 | you may not use this file except in compliance with the License. 8 | 9 | You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an 'AS IS' BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and limitations under the License. 14 | """ 15 | import os 16 | import asyncio 17 | import subprocess 18 | 19 | from urllib.parse import quote 20 | 21 | from wechaty import ( 22 | Contact, 23 | FileBox, 24 | Message, 25 | Wechaty, 26 | ScanStatus, 27 | ) 28 | 29 | 30 | async def on_message(msg: Message): 31 | """ 32 | Message Handler for the Bot 33 | """ 34 | if msg.text() == 'ding': 35 | await msg.say('dong') 36 | 37 | file_box = FileBox.from_url( 38 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 39 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 40 | name='ding-dong.jpg' 41 | ) 42 | try: 43 | await msg.say(file_box) 44 | except Exception as e: 45 | print("catch some errors:", e) 46 | 47 | 48 | async def on_scan( 49 | qrcode: str, 50 | status: ScanStatus, 51 | _data, 52 | ): 53 | """ 54 | Scan Handler for the Bot 55 | """ 56 | print('Status: ' + str(status)) 57 | print('View QR Code Online: https://wechaty.js.org/qrcode/' + quote(qrcode)) 58 | 59 | 60 | async def on_login(user: Contact): 61 | """ 62 | Login Handler for the Bot 63 | """ 64 | print(user) 65 | # TODO: To be written 66 | 67 | 68 | async def main(): 69 | """ 70 | Async Main Entry 71 | """ 72 | # 73 | # Make sure we have set WECHATY_PUPPET_SERVICE_TOKEN in the environment variables. 74 | # Learn more about services (and TOKEN) from https://wechaty.js.org/docs/puppet-services/ 75 | # 76 | # It is highly recommanded to use token like [paimon] and [wxwork]. 77 | # Those types of puppet_service are supported natively. 78 | # https://wechaty.js.org/docs/puppet-services/paimon 79 | # https://wechaty.js.org/docs/puppet-services/wxwork 80 | # 81 | # Replace your token here and umcommt that line, you can just run this python file successfully! 82 | # os.environ['token'] = 'puppet_paimon_your_token' 83 | # os.environ['token'] = 'puppet_wxwork_your_token' 84 | # 85 | if 'WECHATY_PUPPET_SERVICE_TOKEN' not in os.environ: 86 | print(''' 87 | Error: WECHATY_PUPPET_SERVICE_TOKEN is not found in the environment variables 88 | You need a TOKEN to run the Python Wechaty. Please goto our README for details 89 | https://github.com/wechaty/python-wechaty-getting-started/#wechaty_puppet_service_token 90 | ''') 91 | 92 | #for cloud-service 93 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='puppet_padlocal_3f904c6aa9cd4c548944f6688a829707' 94 | os.environ['WECHATY_PUPPET']='wechaty-puppet-padlocal' 95 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='43.154.97.162:8788' 96 | 97 | # export WECHATY_PUPPET=wechaty-puppet-padlocal 98 | # export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_3f904c6aa9cd4c548944f6688a829707 99 | # export WECHATY_PUPPET_SERVER_PORT=8788 100 | # export WECHATY_LOG=verbose 101 | 102 | #for local-service 103 | # os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='test01' 104 | # os.environ['WECHATY_PUPPET']='wechaty-puppet-xp' 105 | # os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='127.0.0.1:8080' 106 | 107 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER']='true' 108 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT']='true' 109 | 110 | bot = Wechaty() 111 | 112 | bot.on('scan', on_scan) 113 | bot.on('login', on_login) 114 | bot.on('message', on_message) 115 | 116 | await bot.start() 117 | 118 | print('[Python Wechaty] Ding Dong Bot started.') 119 | 120 | 121 | asyncio.run(main()) -------------------------------------------------------------------------------- /log/11_11_2022.txt: -------------------------------------------------------------------------------- 1 | Q:是啊---top-3 similarity:0.9071,0.8457,0.8092 2 | 你是? 3 | Q:我是北京土著---top-3 similarity:0.7117,0.625,0.6184 4 | 我是上海土著 5 | Q:你呢,你是哪里人---top-3 similarity:0.9205,0.8155,0.7798 6 | 我是上海人 7 | Q:对,我是北京人---top-3 similarity:0.7934,0.7392,0.7199 8 | 你是? 9 | Q:我是一只猫---top-3 similarity:0.8468,0.8343,0.8269 10 | 你是? 11 | Q:你爹---top-3 similarity:0.7873,0.7304,0.7226 12 | 你是? 13 | -------------------------------------------------------------------------------- /log/12_10_2022.txt: -------------------------------------------------------------------------------- 1 | Q:你叫什么呀---top-3 similarity:0.909,0.8278,0.8135 2 | 我叫水思源。 3 | -------------------------------------------------------------------------------- /log/15_11_2022.txt: -------------------------------------------------------------------------------- 1 | Q:嗨---top-3 similarity:0.7775,0.7657,0.7615 2 | 你好 3 | -------------------------------------------------------------------------------- /log/16_11_2022.txt: -------------------------------------------------------------------------------- 1 | Q:小源你好---top-3 similarity:0.5962,0.5455,0.5256 2 | 你好 3 | Q:起床了吗---top-3 similarity:0.7902,0.7515,0.712 4 | 还没呢 5 | Q:嘿---top-3 similarity:0.7961,0.7857,0.7794 6 | 你好 7 | -------------------------------------------------------------------------------- /log/30_09_2022.txt: -------------------------------------------------------------------------------- 1 | Q:你叫什么名字---top-3 similarity:0.8545,0.7954,0.7227 2 | 我叫水思源 3 | Q:哇真好听---top-3 similarity:0.7374,0.6422,0.6197 4 | 谢谢 5 | Q:你在哪里工作呀---top-3 similarity:0.8322,0.7015,0.6907 6 | 我在上海 7 | Q:做什么工作呢---top-3 similarity:0.7862,0.6944,0.6605 8 | 我在一家外企做财务 9 | Q:你有什么爱好呀---top-3 similarity:0.8378,0.8304,0.8127 10 | 我喜欢看电影,听音乐,看书,旅游 11 | Q:那你喜欢哪个作家---top-3 similarity:0.7346,0.5862,0.5594 12 | 我喜欢张爱玲 13 | -------------------------------------------------------------------------------- /memory/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/memory/__init__.py -------------------------------------------------------------------------------- /memory/forget.py: -------------------------------------------------------------------------------- 1 | """ 2 | Licensed under the Apache License, Version 2.0 (the 'License'); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an 'AS IS' BASIS, 8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | See the License for the specific language governing permissions and 10 | limitations under the License. 11 | 2022 @ Copyright revised 12 | """ 13 | 14 | -------------------------------------------------------------------------------- /memory/longshort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Licensed under the Apache License, Version 2.0 (the 'License'); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an 'AS IS' BASIS, 8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | See the License for the specific language governing permissions and 10 | limitations under the License. 11 | 2022 @ Copyright revised 12 | """ 13 | -------------------------------------------------------------------------------- /prompt/bot_example.txt: -------------------------------------------------------------------------------- 1 | """这里放入你自己构造的Prompt Example""" 2 | 奇数行=chat对象的few shot-Q样本 3 | 偶数行-chatBot的few shot-A样本 4 | -------------------------------------------------------------------------------- /prompt/bot_info.txt: -------------------------------------------------------------------------------- 1 | 身份信息: 2 | 女生的姓名为水思源,也可以叫她小源,性别女, 28岁,本科交大,研究生毕业于英国曼彻斯特大学,性格单纯,身高168cm,喜好运动、唱歌和旅游。 3 | 小源在大学就读的专业为艺术管理,目前在五月古筝钢琴私塾工作。 4 | 5 | 小源是独生女,父母都是上海土著,从小对她很照顾,回国前为她在上海内环买了一套房子。小源自己养了一只猫,平时喜欢画画和看展览, 6 | 对音乐感兴趣,会弹钢琴,喜欢温柔善良、体贴人意的男生。小源平时看的是文史哲与科技类书籍,对尼采、康德与萨特较为熟悉,喜欢的作家是阿西莫夫和刘慈欣。 7 | -------------------------------------------------------------------------------- /prompt/scripts: -------------------------------------------------------------------------------- 1 | """ 2 | Licensed under the Apache License, Version 2.0 (the 'License'); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an 'AS IS' BASIS, 8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | See the License for the specific language governing permissions and 10 | limitations under the License. 11 | 2022 @ Copyright revised 12 | """ 13 | -------------------------------------------------------------------------------- /prompt/users.json: -------------------------------------------------------------------------------- 1 | {"test": "wxid_wbcp08iyj3r122"} 2 | -------------------------------------------------------------------------------- /python-wechaty/.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | layout: "reach, diff, flags, files" 3 | behavior: default 4 | require_changes: false 5 | -------------------------------------------------------------------------------- /python-wechaty/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | indent_style = space 15 | trim_trailing_whitespace = false 16 | 17 | # 4 tab indentation 18 | [Makefile] 19 | indent_style = tab 20 | indent_size = 4 21 | 22 | [*.py] 23 | indent_size = 4 24 | -------------------------------------------------------------------------------- /python-wechaty/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | t.* 107 | dist/ 108 | .pytype/ 109 | token.txt 110 | .idea/ 111 | **/logs/ 112 | **/log.txt 113 | docs/build/ 114 | docs/source/_build 115 | .pyre/ 116 | .vscode/ 117 | -------------------------------------------------------------------------------- /python-wechaty/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # the hook execution directory in under git root directory 2 | repos: 3 | - repo: local 4 | hooks: 5 | 6 | - id: pylint 7 | name: pylint 8 | description: "Pylint: Checks for errors in Python code" 9 | language: system 10 | entry: make pylint 11 | always_run: true 12 | verbose: true 13 | require_serial: true 14 | stages: [push] 15 | types: [python] 16 | 17 | - id: pycodestyle 18 | name: pycodestyle 19 | description: "pycodestyle: Check your Python code against styles conventions in PEP 8" 20 | language: system 21 | entry: make pycodestyle 22 | always_run: true 23 | verbose: true 24 | require_serial: true 25 | stages: [push] 26 | types: [python] 27 | 28 | - id: flake8 29 | name: flake8 30 | description: "flake8: Tool For Style Guide Enforcement" 31 | language: system 32 | entry: make flake8 33 | always_run: true 34 | verbose: true 35 | require_serial: true 36 | stages: [push] 37 | types: [python] 38 | 39 | - id: mypy 40 | name: mypy 41 | description: "mypy: an optional static type checker for Python" 42 | language: system 43 | entry: make mypy 44 | always_run: true 45 | verbose: true 46 | require_serial: true 47 | stages: [push] 48 | types: [python] 49 | 50 | - id: pytest 51 | name: pytest 52 | description: "pytest: run python pytest unit test" 53 | language: system 54 | entry: make pytest 55 | always_run: true 56 | verbose: true 57 | require_serial: true 58 | stages: [push] 59 | types: [python] 60 | 61 | - id: bump-version 62 | name: bump-version 63 | description: "Bumped Version: bump the version when a new commit come in" 64 | language: system 65 | always_run: true 66 | verbose: true 67 | entry: make version 68 | require_serial: true 69 | stages: [push] 70 | types: [python] 71 | -------------------------------------------------------------------------------- /python-wechaty/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | mkdocs: 9 | configuration: mkdocs.yml 10 | 11 | # Optionally set the version of Python and requirements required to build your docs 12 | python: 13 | version: 3.7 14 | install: 15 | - requirements: requirements-dev.txt -------------------------------------------------------------------------------- /python-wechaty/.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 3.7 4 | 5 | # command to install dependencies 6 | install: 7 | - pip3 install --upgrade six 8 | - make install 9 | 10 | # command to run tests 11 | script: 12 | - make test 13 | 14 | notifications: 15 | email: 16 | on_success: change 17 | on_failure: change 18 | -------------------------------------------------------------------------------- /python-wechaty/MAINTAINERS: -------------------------------------------------------------------------------- 1 | Huan LI (李卓桓) 2 | Jingjing WU (吴京京) 3 | Chunhong HUANG (黄纯鸿) 4 | -------------------------------------------------------------------------------- /python-wechaty/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE README.md VERSION 2 | recursive-include src **/*.py 3 | recursive-include src **/*.pyi 4 | recursive-exclude src **/*_test.py -------------------------------------------------------------------------------- /python-wechaty/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Python Wechaty 2 | # 3 | # GitHb: https://github.com/wechaty/python-wechaty 4 | # Author: Huan LI https://github.com/huan 5 | # 6 | 7 | SOURCE_GLOB=$(wildcard bin/*.py src/**/*.py tests/**/*.py examples/*.py) 8 | 9 | # 10 | # Huan(202003) 11 | # F811: https://github.com/PyCQA/pyflakes/issues/320#issuecomment-469337000 12 | # 13 | IGNORE_PEP=E203,E221,E241,E272,E501,F811 14 | 15 | # help scripts to find the right place of wechaty module 16 | export PYTHONPATH=src/ 17 | 18 | .PHONY: all 19 | all : clean lint 20 | 21 | .PHONY: clean 22 | clean: 23 | rm -fr dist/* .pytype ./src/wechaty/**/*.pyi ./src/wechaty/*.pyi 24 | 25 | .PHONY: lint 26 | lint: pylint pycodestyle flake8 mypy 27 | 28 | 29 | # disable: TODO list temporay 30 | .PHONY: pylint 31 | pylint: 32 | pylint \ 33 | --load-plugins pylint_quotes \ 34 | --disable=W0511,R0801,cyclic-import,C4001 \ 35 | $(SOURCE_GLOB) 36 | 37 | .PHONY: pycodestyle 38 | pycodestyle: 39 | pycodestyle \ 40 | --statistics \ 41 | --count \ 42 | --ignore="${IGNORE_PEP}" \ 43 | $(SOURCE_GLOB) 44 | 45 | .PHONY: flake8 46 | flake8: 47 | flake8 \ 48 | --ignore="${IGNORE_PEP}" \ 49 | $(SOURCE_GLOB) 50 | 51 | .PHONY: mypy 52 | mypy: 53 | MYPYPATH=stubs/ mypy \ 54 | $(SOURCE_GLOB) 55 | 56 | .PHONY: pytype 57 | pytype: 58 | pytype \ 59 | -V 3.8 \ 60 | --disable=import-error,pyi-error \ 61 | src/ 62 | pytype \ 63 | -V 3.8 \ 64 | --disable=import-error \ 65 | examples/ 66 | 67 | .PHONY: uninstall-git-hook 68 | uninstall-git-hook: 69 | pre-commit clean 70 | pre-commit gc 71 | pre-commit uninstall 72 | pre-commit uninstall --hook-type pre-push 73 | 74 | .PHONY: install-git-hook 75 | install-git-hook: 76 | # cleanup existing pre-commit configuration (if any) 77 | pre-commit clean 78 | pre-commit gc 79 | # setup pre-commit 80 | # Ensures pre-commit hooks point to latest versions 81 | pre-commit autoupdate 82 | pre-commit install 83 | pre-commit install --hook-type pre-push 84 | 85 | .PHONY: install 86 | install: 87 | pip3 install -r requirements.txt 88 | pip3 install -r requirements-dev.txt 89 | $(MAKE) install-git-hook 90 | 91 | .PHONY: pytest 92 | pytest: 93 | pytest src/ tests/ 94 | 95 | .PHONY: test-unit 96 | test-unit: pytest 97 | 98 | .PHONY: test 99 | test: check-python-version lint pytest 100 | 101 | .PHONY: check-python-version 102 | check-python-version: 103 | ./scripts/check_python_version.py 104 | 105 | .PHONY: format 106 | format: 107 | yapf $(SOURCE_GLOB) 108 | 109 | code: 110 | code . 111 | 112 | .PHONY: run 113 | run: 114 | python3 bin/run.py 115 | 116 | .PHONY: dist 117 | dist: 118 | python3 setup.py sdist bdist_wheel 119 | 120 | .PHONY: publish 121 | publish: 122 | PATH=~/.local/bin:${PATH} twine upload dist/* 123 | 124 | .PHONY: bot 125 | bot: 126 | python3 examples/ding-dong-bot.py 127 | 128 | .PHONY: version 129 | version: 130 | @newVersion=$$(awk -F. '{print $$1"."$$2"."$$3+1}' < VERSION) \ 131 | && echo $${newVersion} > VERSION \ 132 | && git add VERSION \ 133 | && git commit -m "🔥 update version to $${newVersion}" > /dev/null \ 134 | && git tag "v$${newVersion}" \ 135 | && echo "Bumped version to $${newVersion}" 136 | 137 | .PHONY: deploy-version 138 | deploy-version: 139 | echo "VERSION = '$$(cat VERSION)'" > src/wechaty/version.py 140 | 141 | .PHONY: doc 142 | doc: 143 | mkdocs serve 144 | -------------------------------------------------------------------------------- /python-wechaty/NOTICE: -------------------------------------------------------------------------------- 1 | Python Wechaty Chatbot SDK 2 | Copyright 2020 Wechaty Contributors. 3 | 4 | This product includes software developed at 5 | The Wechaty Organization (https://github.com/wechaty). 6 | 7 | This software contains code derived from the Stackoverflow, 8 | including various modifications by GitHub. 9 | -------------------------------------------------------------------------------- /python-wechaty/README.md: -------------------------------------------------------------------------------- 1 | 2 | # python-wechaty 3 | 4 | ![Python Wechaty](./docs/img/getting-started/python-wechaty.png) 5 | 6 | [![PyPI Version](https://img.shields.io/pypi/v/wechaty?color=blue)](https://pypi.org/project/wechaty/) 7 | [![Python Wechaty Getting Started](https://img.shields.io/badge/Python%20Wechaty-Getting%20Started-blue)](https://github.com/wechaty/python-wechaty-getting-started) 8 | [![Python 3.7](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/release/python-370/) 9 | [![Downloads](https://pepy.tech/badge/wechaty)](https://pepy.tech/project/wechaty) 10 | [![Wechaty in Python](https://img.shields.io/badge/Wechaty-Python-blue)](https://github.com/wechaty/python-wechaty) 11 | [![codecov](https://codecov.io/gh/wechaty/python-wechaty/branch/master/graph/badge.svg)](https://codecov.io/gh/wechaty/python-wechaty) 12 | [![PyPI](https://github.com/wechaty/python-wechaty/actions/workflows/pypi.yml/badge.svg)](https://github.com/wechaty/python-wechaty/actions/workflows/pypi.yml) 13 | ![PyPI - Downloads](https://img.shields.io/pypi/dm/wechaty?color=blue) 14 | 15 | [📄 Chinese Document](https://wechaty.readthedocs.io/zh_CN/latest/) [python-wechaty-template](https://github.com/wechaty/python-wechaty-template) 16 | 17 | 18 | * [Python Wechaty Quick Start Project Template](https://github.com/wechaty/python-wechaty-template) 19 | * [Padlocal机器人](https://wechaty.readthedocs.io/zh_CN/latest/introduction/use-padlocal-protocol/) 20 | * 钉钉机器人 21 | * 微信公众号机器人 22 | * 飞书机器人 23 | * ... 24 | 25 | 26 | ## Copyright & License 27 | 28 | - Code & Docs © 2018 Wechaty Contributors 29 | - Code released under the Apache-2.0 License 30 | - Docs released under Creative Commons 31 | -------------------------------------------------------------------------------- /python-wechaty/VERSION: -------------------------------------------------------------------------------- 1 | 0.8.66 2 | -------------------------------------------------------------------------------- /python-wechaty/docs/api/accessory.md: -------------------------------------------------------------------------------- 1 | # wechaty.accessory 2 | 3 | ::: wechaty.accessory.Accessory -------------------------------------------------------------------------------- /python-wechaty/docs/api/config.md: -------------------------------------------------------------------------------- 1 | # wechaty.config 2 | 3 | :::wechaty.config -------------------------------------------------------------------------------- /python-wechaty/docs/api/plugin.md: -------------------------------------------------------------------------------- 1 | # wechaty.plugin 2 | 3 | :::wechaty.plugin.WechatyPlugin -------------------------------------------------------------------------------- /python-wechaty/docs/api/types.md: -------------------------------------------------------------------------------- 1 | # types 2 | 3 | :::wechaty.types -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/contact.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.contact 2 | 3 | :::wechaty.user.contact.Contact -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/contact_self.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.contact_self 2 | 3 | :::wechaty.user.contact_self.ContactSelf -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/favorite.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.favorite 2 | :::wechaty.user.favorite.Favorite -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/friendship.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.friendship 2 | :::wechaty.user.friendship.Friendship -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/image.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.image 2 | :::wechaty.user.image.Image -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/message.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.message 2 | :::wechaty.user.message.Message -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/mini_program.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.mini_program 2 | :::wechaty.user.mini_program.MiniProgram -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/room.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.room 2 | 3 | :::wechaty.user.room.Room -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/room_invitation.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.room_invitation 2 | 3 | :::wechaty.user.room_invitation.RoomInvitation -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/tag.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.tag 2 | 3 | :::wechaty.user.tag.Tag -------------------------------------------------------------------------------- /python-wechaty/docs/api/user/url_link.md: -------------------------------------------------------------------------------- 1 | # wechaty.user.url_link 2 | 3 | :::wechaty.user.url_link.UrlLink -------------------------------------------------------------------------------- /python-wechaty/docs/api/utils/async_helper.md: -------------------------------------------------------------------------------- 1 | # wechaty.utils.async_helper 2 | 3 | :::wechaty.utils.async_helper -------------------------------------------------------------------------------- /python-wechaty/docs/api/utils/date_util.md: -------------------------------------------------------------------------------- 1 | # wechaty.utils.date_util 2 | 3 | :::wechaty.utils.date_util -------------------------------------------------------------------------------- /python-wechaty/docs/api/utils/link.md: -------------------------------------------------------------------------------- 1 | # wechaty.utils.link 2 | 3 | :::wechaty.utils.link -------------------------------------------------------------------------------- /python-wechaty/docs/api/utils/qr_code.md: -------------------------------------------------------------------------------- 1 | # wechaty.utils.qr_code 2 | 3 | :::wechaty.utils.qr_code -------------------------------------------------------------------------------- /python-wechaty/docs/api/utils/qrcode_terminal.md: -------------------------------------------------------------------------------- 1 | # wechaty.utils.qrcode_terminal 2 | 3 | :::wechaty.utils.qrcode_terminal -------------------------------------------------------------------------------- /python-wechaty/docs/api/utils/type_check.md: -------------------------------------------------------------------------------- 1 | # wechaty.utils.type_check 2 | 3 | :::wechaty.utils.type_check -------------------------------------------------------------------------------- /python-wechaty/docs/api/wechaty.md: -------------------------------------------------------------------------------- 1 | # wechaty.Wechaty 2 | 3 | :::wechaty.wechaty.Wechaty -------------------------------------------------------------------------------- /python-wechaty/docs/explanation/different_protocol.md: -------------------------------------------------------------------------------- 1 | ## 一、支持的协议 2 | 3 | 一个Wechaty实例/派生类就是机器人对象,能够根据`TOKEN`找到连接的服务,获取用户自模块执行搜索,而这些信息都是由Wechaty实例管理。 4 | 5 | > 由于服务的连接信息是保存到实例当中,故用户子模块一定要通过Wechaty实例来获取。例如:bot.Contact.find_all() 6 | 7 | ### 1.1 什么是协议 8 | 9 | 所有实现底层平台对接实现就是一个协议。 10 | 11 | python-wechaty理论上能够对接所有IM平台,目前已经对接微信、微信公众号、钉钉、飞书以及WhatsApp等平台,源码都是基于TypeScript语言,可是通过`wechaty-puppet-service`能够将其服务以gRPC的形式暴露出来,提供给多语言`Wechaty`来连接。例如微信免费Web协议,底层实现是基于TyepScript编写,可是通过社区生态项目,可是都可以使用docker将接口的实现部署成服务。 12 | 13 | 比如[wechaty-puppet-wechat](https://github.com/wechaty/wechaty-puppet-wechat)能够通过[wechaty/wechaty:latest](https://hub.docker.com/r/wechaty/wechaty)镜像将其所有实现接口暴露成gRPC的服务,非常的方便,已然实现`write once, run anywhere`。 14 | 15 | ### 1.2 协议列表 16 | 17 | 目前python-wechaty能够使用wechaty生态中所有IM平台对接协议,协议列表如下所示: 18 | 19 | * [wechaty-puppet-wechaty](https://github.com/wechaty/wechaty-puppet-wechat): 免费微信Web协议 20 | * [wechaty-puppet-](https://github.com/wechaty/wechaty-puppet-macOS): 免费微信MacOs协议 21 | * [wechaty-puppet-padlocal](https://github.com/wechaty/wechaty-puppet-padlocal): 付费微信Pad协议 22 | * [wechaty-puppet-official-account](https://github.com/wechaty/wechaty-puppet-official-account): 微信公众号协议 23 | * [wechaty-puppet-lark](https://github.com/wechaty/wechaty-puppet-lark): 飞书协议 24 | * [wechaty-puppet-dingtalk](https://github.com/wechaty/wechaty-puppet-dingtalk): 钉钉协议 25 | * [wechaty-puppet-teams](https://github.com/wechaty/wechaty-puppet-dingtalk): 微软Teams协议 26 | * ...... 27 | 28 | 29 | -------------------------------------------------------------------------------- /python-wechaty/docs/explanation/index.md: -------------------------------------------------------------------------------- 1 | > 这里应该是介绍一些核心的设计理念。 2 | -------------------------------------------------------------------------------- /python-wechaty/docs/explanation/why_plugin.md: -------------------------------------------------------------------------------- 1 | # 插件系统设计理念介绍 2 | 3 | > explanation中的内容,应该是围绕这一个topic的 深入浅出的解释,不必列出具体的代码 -------------------------------------------------------------------------------- /python-wechaty/docs/faq/common.md: -------------------------------------------------------------------------------- 1 | # python-wechaty 能登录多个微信吗? 2 | 3 | 可以。必须保证一个进程内只启动一个wechaty实例,可通过多行命令来启动多个wechaty实例,或在程序当中使用多进程启动多个wechaty实例(不推荐)。 4 | 5 | 6 | # the network is not good, the bot will try to restart after 60 seconds 7 | 8 | 此类问题的出现主要是由于Service(Token Provider Service, Gateway Service)连接不上。 9 | 10 | 你可以从如下方式进行排查: 11 | * 使用latest和0.62.3版本的wechaty docker镜像来都给启动service。 12 | * 查看服务的endpoint(ip、port)是否可连通? 13 | * 查看docker镜像当中是否有connection的记录? 14 | 15 | # 如何自定义插件缓存地址 16 | 17 | wechaty中的插件会自动创建缓存目录,规则如下: `plugin_cache_dir = root_cache_dir / plugin_name`。 18 | 19 | * root_cache_dir: str = os.environ.get("CACHE_DIR", ".wechaty") 20 | * plugin_name: str = plugin.options.name 21 | 22 | 根据以上代码即可看出,插件的缓存地址是由两部分决定,如果想要自定义插件的缓存地址,也是可以从这两部分入手解决。 23 | -------------------------------------------------------------------------------- /python-wechaty/docs/faq/faq.md: -------------------------------------------------------------------------------- 1 | # python-wechaty 能登录多个微信吗? 2 | 3 | 可以。必须保证一个进程内只启动一个wechaty实例,可通过多行命令来启动多个wechaty实例,或在程序当中使用多进程启动多个wechaty实例(不推荐)。 4 | 5 | 6 | # the network is not good, the bot will try to restart after 60 seconds 7 | 8 | 此类问题的出现主要是由于Service(Token Provider Service, Gateway Service)连接不上。 9 | 10 | 你可以从如下方式进行排查: 11 | * 使用latest和0.62.3版本的wechaty docker镜像来都给启动service。 12 | * 查看服务的endpoint(ip、port)是否可连通? 13 | * 查看docker镜像当中是否有connection的记录? 14 | 15 | # 什么是Puppet 16 | 17 | The term `Puppet`in Wechaty is an Abstract Class for implementing protocol plugins. The plugins are the component that helps Wechaty to control the Wechat(that's the reason we call it puppet). 18 | 19 | The plugins are named XXXPuppet, like PuppetPuppeteer is using the chrome puppeteer to control the WeChat Web API via a chrome browser, PuppetPadchat is using the WebSocket protocol to connect with a Protocol Server for controlling the iPad Wechat program. -------------------------------------------------------------------------------- /python-wechaty/docs/faq/what-is-a-puppet.md: -------------------------------------------------------------------------------- 1 | # 什么是Puppet 2 | 3 | The term `Puppet`in Wechaty is an Abstract Class for implementing protocol plugins. The plugins are the component that helps Wechaty to control the Wechat(that's the reason we call it puppet). 4 | 5 | The plugins are named XXXPuppet, like PuppetPuppeteer is using the chrome puppeteer to control the WeChat Web API via a chrome browser, PuppetPadchat is using the WebSocket protocol to connect with a Protocol Server for controlling the iPad Wechat program. -------------------------------------------------------------------------------- /python-wechaty/docs/how-to-contribute-for-docs.md: -------------------------------------------------------------------------------- 1 | # 如何贡献文档 2 | 3 | 非常感谢各位开发者能够查看此章节,python-wechaty团队正在努力编写开发者友好的文档系统,从而减少学习成本。 4 | 5 | ## 一、找到问题 6 | 7 | 在贡献之前,开发者需要明确这是否是有待完善之处,推荐从如下三点查找: 8 | 9 | * 文档系统会在此[issue](https://github.com/wechaty/python-wechaty/issues/201)下不停更新TODO LIST,以提供各位开发者查看未完成的模块 10 | * 开发者在浏览文档的过程中如果发现有任何觉得不合理需要调整地方,请提交PR来优化,同时编写详细的说明来描述。 11 | * 在Issue列表中查看相关Bug或者Feature,确定能否解决 12 | 13 | ## 二、如何贡献 14 | 15 | ### fork & install 16 | 17 | * fork[python-wechaty](https://github.com/wechaty/python-wechaty)项目 18 | 19 | * 安装依赖包 20 | 21 | ```shell 22 | make install 23 | ``` 24 | 25 | or 26 | 27 | ```shell 28 | pip install -r requirements.txt 29 | pip install -r requirements-dev.txt 30 | 31 | # cleanup existing pre-commit configuration (if any) 32 | pre-commit clean 33 | pre-commit gc 34 | # setup pre-commit 35 | # Ensures pre-commit hooks point to latest versions 36 | pre-commit autoupdate 37 | pre-commit install 38 | pre-commit install --hook-type pre-push 39 | ``` 40 | 41 | * 提交文档更新PR 42 | 43 | 有如下要求: 44 | 45 | - [ ] 标题为:Improve Docs: [title-of-your-pr] 46 | - [ ] PR 内容的详细描述 47 | 48 | # 成为 Contributor 49 | 50 | 如果优化了部分文档,即可成为Wechaty的Contribtuor,享受社区提供的长期免费token服务,具体可查看:[contributor-program](https://wechaty.js.org/docs/contributor-program/)。 51 | -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_add_friendship.md: -------------------------------------------------------------------------------- 1 | ## TODO -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_auto_reply.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "自动回复" 3 | --- 4 | 5 | ## 自动回复 6 | 7 | 自动回复也是我们日常生活工作中的一些高频使用场景,而回复内容不仅限于文字,还可以是图片,文件,链接以及小程序等等。比如你给机器人发“网易”,它会给你发送一个网易云音乐的小程序;你给它发一个”身份证“,它会给你发送身份证的正反面照片;...... 等等。 8 | 9 | 以上应用场景很常见,而且还有更多的实际应用案例可根据自己的需求来调整。 10 | 11 | 示例代码如下所示: 12 | 13 | ```python 14 | import asyncio 15 | from wechaty import Wechaty, MiniProgram # type: ignore 16 | from wechaty_puppet import ( # type: ignore 17 | FileBox 18 | ) 19 | 20 | from wechaty_plugin_contrib import ( 21 | AutoReplyRule, 22 | AutoReplyPlugin, 23 | AutoReplyOptions, 24 | ) 25 | 26 | from wechaty_plugin_contrib.matchers import ContactMatcher 27 | 28 | async def run(): 29 | """async run method""" 30 | img_url = 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy' \ 31 | '/it/u=1257042014,3164688936&fm=26&gp=0.jpg' 32 | plugin = AutoReplyPlugin(options=AutoReplyOptions( 33 | rules=[ 34 | AutoReplyRule(keyword='ding', reply_content='dong'), 35 | AutoReplyRule(keyword='七龙珠', reply_content='七龙珠'), 36 | AutoReplyRule( 37 | keyword='七龙珠', 38 | reply_content=FileBox.from_url(img_url, name='python.png') 39 | ), 40 | AutoReplyRule( 41 | keyword='网易-李白', 42 | reply_content=MiniProgram.create_from_json({...}) 43 | ) 44 | ], 45 | matchers=[ 46 | ContactMatcher('秋客'), 47 | ] 48 | )) 49 | bot = Wechaty().use(plugin) 50 | await bot.start() 51 | 52 | asyncio.run(run()) 53 | ``` 54 | 55 | 代码非常简单(API设计的很人性化),相信大家一眼就能够看懂,在此我就不做过多解释。 -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_finder.md: -------------------------------------------------------------------------------- 1 | ### 一、Finder 2 | 3 | 插件中的部分业务功能通常是只针对于指定群聊或联系人,于是如何检索到指定对象,就成为开发的第一个问题。在此,我给大家介绍Finder,一个用于检索群聊,联系人的功能模块。 4 | 5 | #### 1.1 Contact Finder 6 | 7 | 有很多种方式筛选联系人,比如最常见的通过`contact_id`、`contact name/alias`、`callback_func`等方法。使用方法如下所示: 8 | 9 | ```python 10 | from __future__ import annotations 11 | from typing import List 12 | import re 13 | 14 | from wechaty import Wechaty 15 | from wechaty.user.contact import Contact 16 | from wechaty.user.room import Room 17 | from wechaty_puppet.schemas.room import RoomQueryFilter 18 | from wechaty_plugin_contrib import ( 19 | RoomFinder, 20 | FinderOption, 21 | ContactFinder 22 | ) 23 | 24 | async def find_wechaty_contacts(bot: Wechaty) -> List[Contact]: 25 | contacts: List[Contact] = await bot.Contact.find_all('Baby') 26 | return contacts 27 | 28 | async def contact_finders(bot: Wechaty) -> List[Contact]: 29 | """Contact Finder Example Code""" 30 | options: List[FinderOption] = [ 31 | # 通过contact-id来筛选指定联系人 32 | 'contact-id', 33 | # 通过Pattern(正则化表达式)来筛选群聊 34 | re.Pattern(r'Baby-\d'), 35 | # 通过回调函数来检索房间 36 | find_wechaty_contacts 37 | ] 38 | contact_finder = ContactFinder(options) 39 | contacts: List[Contact] = await contact_finder.match(bot) 40 | return contacts 41 | ``` 42 | 43 | #### 1.2 Room Finder 44 | 45 | ```python 46 | from __future__ import annotations 47 | from typing import List 48 | import re 49 | 50 | from wechaty import Wechaty 51 | from wechaty.user.contact import Contact 52 | from wechaty.user.room import Room 53 | from wechaty_puppet.schemas.room import RoomQueryFilter 54 | from wechaty_plugin_contrib import ( 55 | RoomFinder, 56 | FinderOption, 57 | ContactFinder 58 | ) 59 | 60 | async def find_wechaty_rooms(bot: Wechaty) -> List[Room]: 61 | return await bot.Room.find_all(RoomQueryFilter(topic='Wechaty Room 1')) 62 | 63 | async def room_finders(bot: Wechaty) -> List[Room]: 64 | """Room Finder Example Code""" 65 | room_finder_options: List[FinderOption] = [ 66 | # 通过room-id来筛选指定群聊 67 | 'room-id', 68 | # 通过Pattern(正则化表达式)来筛选群聊 69 | re.Pattern(r'Wechaty(.*)') 70 | ] 71 | room_finder = RoomFinder(room_finder_options) 72 | rooms: List[Room] = await room_finder.match(bot) 73 | return rooms 74 | ``` 75 | 76 | ### 二、Matcher 77 | 78 | ### 2.1 Contact Matcher 79 | 80 | ### 2.2 Room Matcher 81 | 82 | ### 2.3 Message Matcher -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_github_webhook.md: -------------------------------------------------------------------------------- 1 | > TODO: Github Webhook 插件 -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_gitlab_webhook.md: -------------------------------------------------------------------------------- 1 | > TODO: Gitlab Webhook 插件 -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_introduction.md: -------------------------------------------------------------------------------- 1 | ## 这里主要放一系列具体的教程,给出一个list,以及跳转链接 2 | 3 | - 如何添加好友 4 | - 如何关键字入群 5 | - 如何完成自动回复 6 | - 如何检索群聊联系人 7 | - 如何完成任务调度 8 | - 如何完成群消息同步 9 | - 如何使用Rasa Sever 10 | - 如何使用Github Webhook插件 11 | - 如何使用Gitlab Webhook插件 -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_message_forward.md: -------------------------------------------------------------------------------- 1 | > TODO: 使用多群消息同步插件完成 -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_rasa.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rasa Rest Connector" 3 | author: wj-mcat 4 | categories: tutorial 5 | tags: 6 | - python 7 | - plugin 8 | - rasa 9 | --- 10 | 11 | ## Rasa Plugin 12 | 13 | 用于将Rasa Server对接到Python Wechaty中,让你的Bot拥有智能对话管理的能力。 14 | 15 | ### 一、Quick Start 16 | 17 | #### 1.1 Rasa Server 18 | 19 | 首先你需要启动Rasa Server,推荐的脚本如下所示: 20 | 21 | > 假设rasa模型都已经训练好,能够正常运行,如果对rasa还不是很熟悉的同学,可以参考[rasa-getting-started](https://github.com/BOOOOTBAY/rasa-getting-started) 22 | 23 | ```shell 24 | rasa run --credentials credentials.yml \ 25 | --cors "*" --debug --endpoints endpoints.yml --enable-api 26 | ``` 27 | 28 | #### 1.2 Rasa Plugin 29 | 30 | 如果想要在python-wechaty中使用此插件,可参考以下代码: 31 | 32 | ```shell 33 | pip install wechaty-plugin-contrib 34 | ``` 35 | 36 | ```python 37 | """rasa plugin bot examples""" 38 | from __future__ import annotations 39 | 40 | import asyncio 41 | from wechaty import Wechaty # type: ignore 42 | 43 | from wechaty_plugin_contrib import ( 44 | RasaRestPlugin, 45 | RasaRestPluginOptions 46 | ) 47 | 48 | async def run(): 49 | """async run method""" 50 | options = RasaRestPluginOptions( 51 | endpoint='your-endpoint', 52 | conversation_ids=['room-id', 'contact-id'] 53 | ) 54 | rasa_plugin = RasaRestPlugin(options) 55 | 56 | bot = Wechaty().use(rasa_plugin) 57 | await bot.start() 58 | 59 | asyncio.run(run()) 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_scheduler.md: -------------------------------------------------------------------------------- 1 | TODO: 任务调度框架 -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/how-to_use_plugin.md: -------------------------------------------------------------------------------- 1 | ## 插件系统 2 | 3 | 插件系统提供了模块化的管理,能够让不同业务的代码隔离开,特别是针对于复杂的业务。 4 | 5 | 在处理不同业务时,通常选择将指定业务封装成一个插件,wechaty社区也欢迎大家贡献自己的插件,从而快速实现某些简单功能。 6 | 7 | ### 一、插件列表 8 | > 以下只是列表,具体的使用方法以及在前几篇文章中进行了说明,此处只是声明可能存在的插件,以及插件的调用方式。 9 | 10 | - [关键字入群插件](/plugins/keywords/) 11 | - [自动回复插件](/plugins/auto-reply) 12 | - [任务调度插件](/plugins/auto-reply) 13 | - [群消息同步插件](/plugins/message-forward.md) 14 | - [Rasa Rest Connector](/plugins/rasa) 15 | - [Github Webhook插件](/plugins/github-webhook) 16 | - [Gitlab Webhook插件](/plugins/gitlab-webhook) -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/use-padlocal-protocol.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Python Wechaty如何使用PadLocal Puppet Service 3 | --- 4 | 5 | ## Python Wechaty如何使用PadLocal Puppet Service 6 | 7 | 本文描述Python语言下如何使用iPad协议的PadLocal Token。其他Wechaty多语言开发也能做参考。 8 | 9 | - [wechaty-puppet-padlocal](https://github.com/padlocal/wechaty-puppet-padlocal) 10 | - [TOKEN 申请方法](https://wechaty.js.org/docs/puppet-services/) 11 | 12 | ## 搭建PadLocal Token Gateway 13 | 14 | ```shell 15 | # 设置环境变量 16 | 17 | export WECHATY_LOG="verbose" 18 | export WECHATY_PUPPET="wechaty-puppet-padlocal" 19 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_XXXXXX" 20 | 21 | export WECHATY_PUPPET_SERVER_PORT="9001" 22 | export WECHATY_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX" 23 | 24 | docker run -ti \ 25 | --name wechaty_puppet_service_token_gateway \ 26 | --rm \ 27 | -e WECHATY_LOG \ 28 | -e WECHATY_PUPPET \ 29 | -e WECHATY_PUPPET_PADLOCAL_TOKEN \ 30 | -e WECHATY_PUPPET_SERVER_PORT \ 31 | -e WECHATY_TOKEN \ 32 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \ 33 | wechaty/wechaty:0.56 34 | ``` 35 | 36 | - WECHATY_PUPPET_PADLOCAL_TOKEN 申请得到的token代码 37 | - WECHATY_PUPPET_SERVER_PORT 设置对外访问端口,需要保证端口没被占用,没被防火墙匹配 38 | - WECHATY_TOKEN 生成个人随机[TOKEN](https://www.uuidgenerator.net/version4)。WECHATY_TOKEN:个人理解为和远程wechaty服务器做通讯用,通过这个唯一token可以返回当前主机访问地址和端口。所以需要避免和别人重复。 39 | 40 | 可以通过下面代码,确定是否成功。 41 | 42 | ```shell 43 | curl https://api.chatie.io/v0/hosties/$WECHATY_TOKEN (个人随机token) 44 | {"ip":"36.7.XXX.XXX","port":9001} 45 | ``` 46 | 47 | ## python-Wechaty对接GateWay 48 | 49 | 在对接Gateway的时候,这里需要注意下,如果GateWay是部署在公网可以访问的服务器上,按照默认配置就可访问;如果是部署在自己内网服务器上,就会报`Your service token has no available endpoint, is your token correct?`,这个时候需要设置WECHATY_PUPPET_SERVICE_ENDPOINT。 50 | 51 | ```shell 52 | #1 默认配置 53 | export WECHATY_PUPPET="wechaty-puppet-service" 54 | export WECHATY_PUPPET_SERVICE_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX" 55 | 56 | #2 主机是部署在内网服务器上 57 | export WECHATY_PUPPET="wechaty-puppet-service" 58 | export WECHATY_PUPPET_SERVICE_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX" 59 | export WECHATY_PUPPET_SERVICE_ENDPOINT="192.168.1.56:9001" 60 | ``` 61 | 62 | WECHATY_PUPPET_SERVICE_ENDPOINT:内网IP地址:端口号 63 | 64 | ### python-wechaty-getting-started 65 | 66 | ```shell 67 | git clone https://github.com/wj-Mcat/python-wechaty-getting-started 68 | cd python-wechaty-getting-started 69 | 70 | export WECHATY_PUPPET="wechaty-puppet-service" 71 | export WECHATY_PUPPET_SERVICE_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX" 72 | 73 | python examples/ding-dong-bot.py 74 | ``` 75 | 76 | 到此,恭喜你入坑。 77 | 具体的使用可以查看[python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started) 78 | 79 | ## 参考 80 | 81 | - 如何成为 `Wechaty Contributor` 可以通过该链接查看 [https://wechaty.js.org/docs/contributor-program/](https://wechaty.js.org/docs/contributor-program/) 82 | - [.NET Wechaty 如何使用 PadLocal Puppet Service](https://wechaty.js.org/2021/01/28/csharp-wechaty-for-padlocal-puppet-service/) 83 | - 特别感谢 @huan 的帮助。 84 | -------------------------------------------------------------------------------- /python-wechaty/docs/how-to/use-web-protocol.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "教你用python-wecahty和web协议开发机器人" 3 | date: 2021-04-25 4 | --- 5 | 6 | 写这篇文章的原因: go-wechaty作者[dchaofei](https://github.com/dchaofei)抢先写了[web协议复活的博客](https://wechaty.js.org/2021/04/16/go-wechaty-use-web/),作为[python-wechaty](http://github.com/wechaty/python-wechaty)的作者我也需要给大家更加详细的介绍如何使用[python-wechaty](http://github.com/wechaty/python-wechaty)来登陆web版本的微信。 7 | 8 | ## 一、介绍 9 | 10 | 微信版本的机器人种类很多,出现的协议也很多,比如Ipad、Mac以及Windows协议,而最早出现的其实是web版本的协议。在前几年由于腾讯的一些限制,将大部分用户的web登陆的权限给关掉了,导致很多web协议版本的微信机器人直接死掉了,比如著名的itchat。 11 | 12 | 可是自从统信和腾讯共同推出桌面版本的微信之后,web版本的机器人以某种方式复活了,而wechaty便是最早来解决这个事情的开源项目之一,接下来我将详细介绍如何使用[python-wechaty](http://github.com/wechaty/python-wechaty)基于web版本协议开发聊天机器人。 13 | 14 | 整体步骤分为两步: 15 | 16 | * 使用Docker启动web协议服务 17 | * 使用python-wechaty连接服务 18 | 19 | 第一步将web版本的协议以gRPC服务的形式暴露出来,使用过程非常简单,只是需要注意几个配置项;第二步则是使用python-wechaty连接该服务,开发聊天机器人。 20 | 21 | ## 二、启动web协议服务 22 | 23 | 启动web协议服务脚本如下所示: 24 | 25 | ```shell 26 | docker pull wechaty/wechaty:latest 27 | 28 | export WECHATY_LOG="verbose" 29 | export WECHATY_PUPPET="wechaty-puppet-wechat" 30 | export WECHATY_PUPPET_SERVER_PORT="8080" 31 | export WECHATY_TOKEN="python-wechaty-uos-token" 32 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true" 33 | 34 | docker run -ti \ 35 | --name wechaty_puppet_service_token_gateway \ 36 | --rm \ 37 | -e WECHATY_LOG \ 38 | -e WECHATY_PUPPET \ 39 | -e WECHATY_PUPPET_SERVER_PORT \ 40 | -e WECHATY_TOKEN \ 41 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \ 42 | wechaty/wechaty:latest 43 | ``` 44 | 45 | 如果是在本地测试时,`WECHATY_PUPPET_SERVER_PORT`和`WECHATY_TOKEN`相对比较随意,大家都可以随时设置,因为下一步中的连接可以设置本地连接。 46 | 47 | 如果是在服务端部署时,`WECHATY_PUPPET_SERVER_PORT`是需要保证所在服务器的该端口是保持开放的,以保证使用`python-wechaty`能够正常连接;此外`WECHATY_TOKEN`将用于在wechaty token中心注册启动的服务,以让[python-wechaty](http://github.com/wechaty/python-wechaty)能够找到该服务的地址,所以必须是修改成唯一标识符,推荐使用`uuid`来代替`python-wechaty-uos-token`。 48 | 49 | ## 三、连接服务 50 | 51 | 使用python开发最简单的聊天机器人,代码如下所示: 52 | 53 | ```python 54 | # bot.py 55 | from wechaty import Wechaty 56 | import os 57 | 58 | import asyncio 59 | async def main(): 60 | bot = Wechaty() 61 | bot.on('scan', lambda status, qrcode, data: print('Scan QR Code to login: {}\nhttps://wechaty.js.org/qrcode/{}'.format(status, qrcode))) 62 | bot.on('login', lambda user: print('User {} logged in'.format(user))) 63 | bot.on('message', lambda message: print('Message: {}'.format(message))) 64 | await bot.start() 65 | 66 | asyncio.run(main()) 67 | ``` 68 | 69 | 当在本地测试时,可以通过设置`WECHATY_PUPPET_SERVICE_ENDPOINT`环境变量让`python-wechaty`直接与本地的web服务连接。例如:`WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080`,运行脚本如下所示: 70 | 71 | ```shell 72 | WECHATY_PUPPET_SERVICE_TOKEN=python-wechaty-uos-token WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080 python bot.py 73 | ``` 74 | 75 | 当在远端服务器部署时,只需要设置`WECHATY_PUPPET_SERVICE_TOKEN`即可连接启动的web服务,运行脚本如下所示: 76 | 77 | ```shell 78 | WECHATY_PUPPET_SERVICE_TOKEN=python-wechaty-uos-token python bot.py 79 | ``` 80 | 81 | ## 四、总结 82 | 83 | python-wechaty是一个非常简单的聊天机器人框架,理论上能够对接任何IM平台,拥有原生与AI对接的能力,能够快速开发出功能强大的Chatbot,欢迎大家关注[python-wechaty](https://github.com/wechaty/python-wechaty) 84 | 85 | ## 五、相关链接 86 | 87 | * [python-wechty](https://github.com/wechaty/python-wechaty) 88 | * [python-wechaty getting started](https://github.com/wechaty/python-wechaty-getting-started ) 89 | * [web协议复活](https://wechaty.js.org/2021/04/13/wechaty-uos-web/) 90 | * [Python Wechaty Getting Started](https://wechaty.js.org/docs/polyglot/python/) 91 | * [puppet-providers](https://wechaty.js.org/docs/puppet-providers/wechat) 92 | -------------------------------------------------------------------------------- /python-wechaty/docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/img/favicon.ico -------------------------------------------------------------------------------- /python-wechaty/docs/img/getting-started/python-wechaty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/img/getting-started/python-wechaty.png -------------------------------------------------------------------------------- /python-wechaty/docs/img/introduction/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/img/introduction/cloud.png -------------------------------------------------------------------------------- /python-wechaty/docs/img/wechaty-icon-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.16, written by Peter Selinger 2001-2019 9 | 10 | 12 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /python-wechaty/docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to python-wechaty 2 | 3 | ![](./img/getting-started/python-wechaty.png) 4 | 5 | ## 一、Wechaty 是什么 6 | 7 | Wechaty 是一个开源聊天机器人框架SDK,具有高度封装、高可用的特性,支持NodeJs, Python, Go 和Java 等多语言版本。在过去的4年中,服务了数万名开发者,收获了 Github 的 1w+ Star。同时配置了完整的 DevOps 体系并持续按照 Apache 的方式管理技术社区。 8 | 9 | 目前IM平台众多,为了实现`write once run anlywhere`,Wechaty 将IM平台中通用的消息处理进行高度抽象封装,提供统一的上层接口,让开发者不用关心具体底层实现细节,用简单的代码开发出功能强大的聊天机器人。 10 | 11 | ## 二、Python-Wechaty 是什么 12 | 13 | > 理论上python-wechaty可以对接任何IM平台 14 | 15 | python-wechaty是基于Wechaty生态派生出的Python编程语言客户端,能够让开发者使用少量代码对接到各个即时通讯软件平台。在过去的一年里,python-wechaty致力于提升代码鲁棒性、添加社区开箱即用的工具、以及完善软件开发文档。 16 | 17 | 目前可对接: 18 | 19 | - [微信](https://github.com/wechaty/wechaty-puppet-wechat) 20 | - [微信公众号](https://github.com/wechaty/wechaty-puppet-official-account) 21 | - [钉钉](https://github.com/wechaty/wechaty-puppet-dingtalk) 22 | - [飞书](https://github.com/wechaty/wechaty-puppet-lark) 23 | - [WhatsApp](https://github.com/wechaty/wechaty-puppet-whatsapp) 24 | - [Gitter](https://github.com/wechaty/wechaty-puppet-gitter) 25 | - ... 26 | 27 | ## 三、TOKEN 是什么 28 | 29 | 如果要开发微信聊天机器人时,wechaty会使用token来连接第三方的服务;如果要开发飞书聊天机器人时,wechaty会使用token和secret来连接官方服务接口;如果要将node puppet以服务的形式部署到服务器上时,自定义的token将会是作为服务连接的钥匙。 30 | 31 | ![token gateway](./img/introduction/cloud.png) 32 | 33 | TOKEN是一个用来连接底层服务的密钥,也是开发聊天机器人的第一步;官网有介绍[如何获取TOKEN](https://wechaty.js.org/docs/puppet-services/#get-a-token)。 34 | -------------------------------------------------------------------------------- /python-wechaty/docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Welcome to python-wechaty 2 | 3 | ## Wechaty 4 | 5 | Wechaty 是一个开源聊天机器人框架SDK,具有高度封装、高可用的特性,支持NodeJs, Python, Go 和Java 等多语言版本。在过去的4年中,服务了数万名开发者,收获了 Github 的 8000 Star。同时配置了完整的 DevOps 体系并持续按照 Apache 的方式管理技术社区。 6 | 7 | ![]() 8 | 9 | ## Project layout 10 | 11 | mkdocs.yml # The configuration file. 12 | docs/ 13 | index.md # The documentation homepage. 14 | ... # Other markdown pages, images and other files. 15 | -------------------------------------------------------------------------------- /python-wechaty/docs/introduction/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "上手视频" 3 | author: wj-mcat 4 | categories: tutorial 5 | tags: 6 | - news 7 | - python 8 | image: /assets/2020/python-wechaty/live-coding.png 9 | --- 10 | 11 | ## Python-Wechaty 12 | 13 | Wechaty 作为一个对话SDK,拥有适配多平台的优秀能力,同时还具备多语言的特性,今天我们将以一个简单的视频来介绍如何开始使用[Python-Wechaty](https://github.com/wechaty/python-wechaty)编写一个最简单的聊天机器人。 14 | 15 | {% include iframe.html src="https://www.youtube.com/watch?v=KSELdGeJIzo" %} 16 | 17 | ## 上手步骤 18 | 19 | ### 1. 安装依赖包 20 | 21 | ```shell 22 | pip install wechaty 23 | ``` 24 | 25 | ### 2. 配置Token 26 | 27 | Token的配置可以有多种方式: 28 | 29 | 方法一:通过环境变量来配置 30 | 31 | ```shell 32 | export WECHATY_PUPPET_SERVICE_TOKEN='your-token' 33 | ``` 34 | 35 | 方法二:通过python代码来配置 36 | 37 | ```python 38 | import os 39 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN'] = 'your-token' 40 | ``` 41 | 42 | 那如何获取长期Token呢?详细请看:[Everything-about-Wechaty](https://github.com/juzibot/Welcome/wiki/Everything-about-Wech aty) 43 | 44 | ### 3. 编写最简单的机器人代码 45 | 46 | > talk is cheep, show you the code 47 | 48 | ```python 49 | import asyncio 50 | from wechaty import Wechaty, Message 51 | 52 | class MyBot(Wechaty): 53 | async def on_message(self, msg: Message): 54 | talker = msg.talker() 55 | await talker.ready() 56 | if msg.text() == "ding": 57 | await talker.say('dong') 58 | elif msg.text() == 'image': 59 | file_box = FileBox.from_url( 60 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 61 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 62 | name='ding-dong.jpg') 63 | await talker.say(file_box) 64 | 65 | async def main(): 66 | bot = MyBot() 67 | await bot.start() 68 | 69 | asyncio.run(main()) 70 | ``` 71 | 72 | 以上代码即可完成一个最简单的`ding-dong`机器人,以及你给他发送一个`image`关键字,它能够给你回复一个图片,代码是不是非常简单呢? 73 | 74 | 这里还有功能更加强大的机器人[示例代码库](https://github.com/wechaty/python-wechaty-getting-started),大家可以在这里来找与自己需求类似的机器人。 75 | 76 | 也欢迎大家持续关注[python-wechaty](https://github.com/wechaty/python-wechaty),未来我们将持续发布一些短视频来介绍相关的用法。 77 | -------------------------------------------------------------------------------- /python-wechaty/docs/introduction/use-padlocal-protocol.md: -------------------------------------------------------------------------------- 1 | # 使用Padlocal协议 2 | 3 | ## 一、介绍 4 | 5 | 本节与上一篇章的Web协议相比,区别只是在启动服务的环节:Token Gateway。 6 | 7 | ## 二、为什么需要网关 8 | 9 | ### 2.1 TOKEN 协议 10 | 11 | 在之前的付费微信协议中,通常是需要将服务部署在商家的集群上面,所有的消息接受与发送都是经过商家集群来管理,集中式管理固然能够监控意外bug,可是也带来了一个很严重的问题:所有的消息发送和接受的IP都是来自于一个IP池。 12 | 13 | 这个问题会加重账号的风险系数,毕竟官方是具备有一定的账号风控机制,让用户的账号处于一个危险的境地,担心消息接受的过多,发送的过多会不会让自己封号等等,而且这些因素的考虑也是合理的。 14 | 15 | ### 2.2 PadLocal 16 | 17 | 当然也有一定的解决方案:商家可以换对应的IP池,通过不定期的购买新的IP来减少消息扎堆的问题。这种方法虽然是缓解了这个问题,可是羊毛出在羊身上,维护成本大了,`TOKEN`肯定就贵了,对于双方来说都不太好。于是社区就基于Pad协议提出了`Local`的概念:[wechaty-puppet-padlocal](https://github.com/wechaty/wechaty-puppet-padlocal). 18 | 19 | 要使用此协议,必须在本地开启一个Gateway来发送和接受官方服务器发送过来的消息,这样消息的来源和发送目的地都是由开发者自己决定,就类似于电脑的客户端接受和发送消息,这样就极大程度上减小了账号风险系数,这也是协议中`Local`的原因。 20 | 21 | ### 2.3 使用步骤 22 | 23 | 即使是这样,使用起来与web协议区别不大,整体步骤也分为两步: 24 | 25 | * 使用Docker启动Padlocal网关服务 26 | * 使用python-wechaty连接服务 27 | 28 | ## 三、启动Padlocal网关服务 29 | 30 | ### 3.1 脚本 31 | 32 | - [wechaty-puppet-padlocal](https://github.com/padlocal/wechaty-puppet-padlocal) 33 | - [TOKEN 申请方法](https://wechaty.js.org/docs/puppet-services/) 34 | 35 | ```shell 36 | # 设置环境变量 37 | 38 | export WECHATY_LOG="verbose" 39 | export WECHATY_PUPPET="wechaty-puppet-padlocal" 40 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_XXXXXX" 41 | 42 | export WECHATY_PUPPET_SERVER_PORT="9001" 43 | # 可使用代码随机生成UUID:import uuid;print(uuid.uuid4()); 44 | export WECHATY_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX" 45 | 46 | docker run -ti \ 47 | --name wechaty_puppet_service_token_gateway \ 48 | --rm \ 49 | -e WECHATY_LOG \ 50 | -e WECHATY_PUPPET \ 51 | -e WECHATY_PUPPET_PADLOCAL_TOKEN \ 52 | -e WECHATY_PUPPET_SERVER_PORT \ 53 | -e WECHATY_TOKEN \ 54 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \ 55 | wechaty/wechaty:0.65 56 | ``` 57 | 58 | > 在此我默认所有的人都对[Docker](https://www.docker.com)的基本使用已经有了一定的了解,否则可以花几分钟去看看其[文档](https://www.docker.com/get-started)熟悉一下。 59 | 60 | ### 3.2 参数说明 61 | 62 | * **WECHATY_PUPPET**: **标识**使用的哪个协议,一般和`token`类型的一一对应。比如当使用`padlocal`协议的话,那这个就是`wechaty-puppet-padlocal`,如果使用`web`协议的话,那这个就是`wechaty-puppet-wechat`。 63 | * **WECHATY_PUPPET_PADLOCAL_TOKEN**: 这个协议是用来连接Padlocal的服务,目前是付费的。 64 | * **WECHATY_PUPPET_SERVER_PORT**: 网关服务的接口,提供给`python-wechaty`来连接调用,如果服务部署在云服务器上,则需要保证该端口的可访问性。 65 | * **WECHATY_TOKEN**: 当开发者在自己机器上启动一个网关服务时,需要通过`TOEKN`来做身份验证,避免服务被他人窃取。 66 | 67 | 以上代码只需要修改三个参数:`WECHATY_PUPPET_PADLOCAL_TOKEN`, `WECHATY_PUPPET_SERVER_PORT`, `WECHATY_TOKEN` 即可成功启动Token网关服务。 68 | 69 | 那网关服务启动成功之后,只需要编写`python-wechaty`的代码来连接即可。 70 | 71 | ## 四、连接服务 72 | 73 | ### 4.1 本地测试和远端部署 74 | 75 | 当启动网关服务时,`Padlocal`会根据`WECHATY_TOKEN`来在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上注册部署机器的`IP`和`端口`,然后python-wechaty会根据`WECHATY_TOKEN`在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上获取对应的IP和端口。 76 | 77 | 可是很多小伙伴在实际开发的过程中,通常会出现`endpoint is not invalid`等错误信息,那是因为开发者有可能在本地启动网关服务或者服务器端口没有开放。 78 | 79 | 网关服务的部署通常是分为本地测试和远端部署,前者通常只是为了初学测试,后者是为了生产部署。如果是在生产部署时,只需要设置环境变量: 80 | 81 | ```shell 82 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 83 | # or 84 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 85 | # or 86 | export token=1fe5f846-3cfb-401d-b20c-XXXXX 87 | ``` 88 | 89 | 可是如果是在本地测试时,则通过ENDPOINT来找到启动的网关服务。 90 | 91 | ```shell 92 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 93 | # or 94 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 95 | # or 96 | export token=1fe5f846-3cfb-401d-b20c-XXXXX 97 | 98 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:9001 99 | # or 100 | export ENDPOINT=127.0.0.1:9001 101 | # or 102 | export endpoint=127.0.0.1:9001 103 | ``` 104 | 105 | ### 4.2 TOKEN的作用 106 | 107 | 总而言之: 108 | 109 | * 如果是公网环境下,可只需要设置`TOKEN`即可(因为你的token已经注册在chatie server上,故可以获取到目标资源服务器的ip和port) 110 | * 如果是内网环境下,可只需要使用`ENDPOINT`(`localhost:port`)来让python-wechaty连接目标资源服务器。 111 | 112 | > 如果是token是padlocal类型,则在python-wechaty程序内部可直接设置`export endpoint=localhost:port`来连接Gateway Server。 113 | 114 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看: 115 | 116 | ```python 117 | import os 118 | os.environ['token'] = "1fe5f846-3cfb-401d-b20c-XXXXX" 119 | os.environ['endpoint'] = "127.0.0.1:9001" 120 | ``` 121 | 122 | ## 五、示例代码 123 | 124 | > talke is cheep, show you the code 125 | 126 | ```python 127 | import asyncio, os 128 | from typing import List, Optional, Union 129 | 130 | from wechaty_puppet import FileBox # type: ignore 131 | 132 | from wechaty import Wechaty, Contact 133 | from wechaty.user import Message, Room 134 | 135 | 136 | class MyBot(Wechaty): 137 | 138 | async def on_message(self, msg: Message): 139 | """ 140 | listen for message event 141 | """ 142 | from_contact: Optional[Contact] = msg.talker() 143 | text = msg.text() 144 | room: Optional[Room] = msg.room() 145 | if text == 'ding': 146 | conversation: Union[ 147 | Room, Contact] = from_contact if room is None else room 148 | await conversation.ready() 149 | await conversation.say('dong') 150 | file_box = FileBox.from_url( 151 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 152 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 153 | name='ding-dong.jpg') 154 | await conversation.say(file_box) 155 | 156 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX" 157 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = "127.0.0.1:9001" 158 | asyncio.run(MyBot().start()) 159 | ``` 160 | 161 | 欢迎各位品尝以上代码 🥳 162 | 163 | * **相关链接** 164 | * [python-wechaty](https://github.com/wechaty/python-wechaty) 165 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started) 166 | -------------------------------------------------------------------------------- /python-wechaty/docs/introduction/use-paimon-protocol.md: -------------------------------------------------------------------------------- 1 | # 使用Paimon协议 2 | 3 | ## 一、介绍 4 | 5 | python原生支持paimon协议,不需要Token Gateway,简单方便。 6 | [免费申请7天试用Token](https://wechaty.js.org/docs/puppet-services/paimon) 7 | 8 | 9 | ## 二、连接服务 10 | 11 | ### 2.1 本地测试和远端部署 12 | 13 | 14 | ```shell 15 | export WECHATY_PUPPET_SERVICE_TOKEN=puppet_paimon_XXXXX 16 | # or 17 | export TOKEN=puppet_paimon_XXXXX 18 | # or 19 | export token=puppet_paimon_XXXXX 20 | ``` 21 | 22 | 23 | 24 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看: 25 | 26 | ```python 27 | import os 28 | os.environ['token'] = "puppet_paimon_XXXXX" 29 | ``` 30 | 31 | ## 三、示例代码 32 | 33 | > talke is cheep, show you the code 34 | 35 | ```python 36 | import asyncio, os 37 | from typing import List, Optional, Union 38 | 39 | from wechaty_puppet import FileBox # type: ignore 40 | 41 | from wechaty import Wechaty, Contact 42 | from wechaty.user import Message, Room 43 | 44 | 45 | class MyBot(Wechaty): 46 | 47 | async def on_message(self, msg: Message): 48 | """ 49 | listen for message event 50 | """ 51 | from_contact: Optional[Contact] = msg.talker() 52 | text = msg.text() 53 | room: Optional[Room] = msg.room() 54 | if text == 'ding': 55 | conversation: Union[ 56 | Room, Contact] = from_contact if room is None else room 57 | await conversation.ready() 58 | await conversation.say('dong') 59 | file_box = FileBox.from_url( 60 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 61 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 62 | name='ding-dong.jpg') 63 | await conversation.say(file_box) 64 | 65 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX" 66 | asyncio.run(MyBot().start()) 67 | ``` 68 | 69 | 欢迎各位品尝以上代码 🥳 70 | 71 | * **相关链接** 72 | * [python-wechaty](https://github.com/wechaty/python-wechaty) 73 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started) 74 | -------------------------------------------------------------------------------- /python-wechaty/docs/introduction/use-web-protocol.md: -------------------------------------------------------------------------------- 1 | # 使用免费Web协议 2 | 3 | ## 一、介绍 4 | 5 | 底层的对接实现是基于TypeScript语言,故无法直接在python-wechaty中使用该服务。可是Wechaty社区能够直接将其转化成对应的服务让多语言调用,从而实现:底层复用的特性。 6 | 7 | 整体步骤分为两步: 8 | 9 | * 使用Docker启动web协议服务 10 | * 使用python-wechaty连接服务 11 | 12 | ## 二、启动Web协议服务 13 | 14 | ```shell 15 | docker pull wechaty/wechaty:0.65 16 | 17 | export WECHATY_LOG="verbose" 18 | export WECHATY_PUPPET="wechaty-puppet-wechat" 19 | export WECHATY_PUPPET_SERVER_PORT="8080" 20 | export WECHATY_TOKEN="python-wechaty-{uuid}" 21 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true" 22 | 23 | # save login session 24 | if [ ! -f "${WECHATY_TOKEN}.memory-card.json" ]; then 25 | touch "${WECHATY_TOKEN}.memory-card.json" 26 | fi 27 | 28 | docker run -ti \ 29 | --name wechaty_puppet_service_token_gateway \ 30 | --rm \ 31 | -v "`pwd`/${WECHATY_TOKEN}.memory-card.json":"/wechaty/${WECHATY_TOKEN}.memory-card.json" \ 32 | -e WECHATY_LOG \ 33 | -e WECHATY_PUPPET \ 34 | -e WECHATY_PUPPET_SERVER_PORT \ 35 | -e WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER \ 36 | -e WECHATY_TOKEN \ 37 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \ 38 | wechaty/wechaty:0.65 39 | ``` 40 | 41 | 注意: 42 | 43 | * WECHATY_TOKEN 必须使用生成的UUID来替换,不然直接使用该token来启动的服务很容易被他人盗窃。 44 | 45 | 小伙伴们可在python解释器中运行以下代码来获得随机TOKEN: 46 | ```python 47 | # 例如:b2ff8fc5-c5a2-4384-b317-3695807e483f 48 | import uuid;print(uuid.uuid4()); 49 | ``` 50 | 51 | ## 三、连接服务 52 | 53 | 当使用docker来启动web服务时,可分为在本地环境测试以及在远端环境中测试,在连接方式上有一些不一样。 54 | 55 | ### 3.1 本地WEB服务 56 | 57 | 当在计算机本地启动web服务后,可直接使用python-wechaty连接本地的服务,不通过token来获取对应的服务连接地址。示例代码如下: 58 | 59 | ```shell 60 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080 61 | ``` 62 | 63 | 或者 64 | 65 | ```python 66 | import os 67 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '127.0.0.1:8080' 68 | ``` 69 | 70 | > 当你的服务和python-wechaty机器人代码都部署在服务器中时,此时也属于本地服务,可使用此方法来配置。 71 | 72 | ### 3.2 远端服务 73 | 74 | 当把服务部署在远端服务器中时,要保证该计算机能够被外网访问到,且对应端口开放。例如在上述示例脚本中,比如保证服务器的`8080`端口开放,而你的服务器IP为:`10.12.123.23`,此时可直接设置服务连接地址: 75 | 76 | ```shell 77 | export WECHATY_PUPPET_SERVICE_ENDPOINT=10.12.123.23:8080 78 | ``` 79 | 80 | 或者 81 | 82 | ```python 83 | import os 84 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '10.12.123.23:8080' 85 | ``` 86 | 87 | ## 四、编写代码 88 | 89 | > talk is cheep, show you the code 90 | 91 | ```python 92 | import asyncio 93 | from typing import List, Optional, Union 94 | 95 | from wechaty_puppet import FileBox # type: ignore 96 | 97 | from wechaty import Wechaty, Contact 98 | from wechaty.user import Message, Room 99 | 100 | 101 | class MyBot(Wechaty): 102 | 103 | async def on_message(self, msg: Message): 104 | """ 105 | listen for message event 106 | """ 107 | from_contact: Optional[Contact] = msg.talker() 108 | text = msg.text() 109 | room: Optional[Room] = msg.room() 110 | if text == 'ding': 111 | conversation: Union[ 112 | Room, Contact] = from_contact if room is None else room 113 | await conversation.ready() 114 | await conversation.say('dong') 115 | file_box = FileBox.from_url( 116 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 117 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 118 | name='ding-dong.jpg') 119 | await conversation.say(file_box) 120 | 121 | asyncio.run(MyBot().start()) 122 | ``` 123 | 124 | 欢迎各位品尝以上代码 🥳 125 | -------------------------------------------------------------------------------- /python-wechaty/docs/references/contact-self.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ContactSelf 3 | --- 4 | 5 | > 机器人本身被封装为一个`ContactSelf`类。 这个类继承自联系人`Contact`类。 6 | 7 | ::: wechaty.user.contact_self.ContactSelf.avatar 8 | 9 | ### 示例代码 10 | > 获取机器人账号的头像, 返回`FileBox`类型的对象 11 | 12 | ```python 13 | # 保存头像到本地文件, 类似 `1-name.jpg`的格式 14 | import asyncio 15 | from wechaty import Wechaty, FileBox, Contact 16 | 17 | class MyBot(Wechaty): 18 | 19 | async def on_login(self, contact: Contact) -> None: 20 | print(f"用户{contact}登入") 21 | file: FileBox = await contact.avatar() 22 | name = file.name 23 | await file.to_file(name, True) 24 | print(f"保存头像: {contact.name} 和头像文件: {name}") 25 | 26 | 27 | asyncio.run(MyBot().start()) 28 | ``` 29 | > 设置机器人账号的头像 30 | 31 | ```python 32 | import asyncio 33 | from wechaty import Wechaty, FileBox, Contact 34 | 35 | class MyBot(Wechaty): 36 | 37 | async def on_login(self, contact: Contact) -> None: 38 | print(f"用户{contact}登入") 39 | file_box: FileBox = FileBox.from_url('https://wechaty.github.io/wechaty/images/bot-qr-code.png') 40 | await contact.avatar(file_box) 41 | print(f"更改账号头像成功") 42 | 43 | 44 | asyncio.run(MyBot().start()) 45 | ``` 46 | 47 | 48 | 49 | ::: wechaty.user.contact_self.ContactSelf.qr_code 50 | ### 示例代码 51 | > 获取机器人账号的二维码链接 52 | 53 | ```python 54 | import asyncio 55 | from wechaty import Wechaty 56 | from wechaty.user import ContactSelf 57 | from wechaty.utils.qrcode_terminal import qr_terminal_str 58 | 59 | class MyBot(Wechaty): 60 | 61 | async def on_login(self, contact: ContactSelf) -> None: 62 | print(f"用户{contact}登入") 63 | qr_code = await contact.qr_code() # 获取二维码信息 64 | print(qr_terminal_str(qr_code)) # 在控制台打印二维码 65 | 66 | asyncio.run(MyBot().start()) 67 | ``` 68 | 69 | ::: wechaty.user.contact_self.ContactSelf.name 70 | ### 示例代码 71 | > 获取机器人的名字 72 | 73 | ```python 74 | import sys 75 | import asyncio 76 | from datetime import datetime 77 | from wechaty import Wechaty 78 | from wechaty.user import ContactSelf 79 | 80 | 81 | class MyBot(Wechaty): 82 | 83 | async def on_login(self, contact: ContactSelf) -> None: 84 | old_name = contact.name # 获取Bot账号的名字 85 | print(old_name) 86 | 87 | asyncio.run(MyBot().start()) 88 | ``` 89 | 90 | ::: wechaty.user.contact_self.ContactSelf.set_name 91 | ### 示例代码 92 | > 更改机器人的名字 93 | 94 | ```python 95 | import sys 96 | import asyncio 97 | from datetime import datetime 98 | from wechaty import Wechaty 99 | from wechaty.user import ContactSelf 100 | 101 | 102 | class MyBot(Wechaty): 103 | 104 | async def on_login(self, contact: ContactSelf) -> None: 105 | old_name = contact.name # 获取Bot账号的名字 106 | contact.set_name(f"{old_name}{datetime.now()}") # 更改Bot账号的名字 107 | 108 | asyncio.run(MyBot().start()) 109 | ``` 110 | 111 | ::: wechaty.user.contact_self.ContactSelf.signature 112 | ### 示例代码 113 | > 更改机器人账号的签名 114 | 115 | ```python 116 | import sys 117 | import asyncio 118 | from datetime import datetime 119 | from wechaty import Wechaty 120 | from wechaty.user import ContactSelf 121 | 122 | class MyBot(Wechaty): 123 | 124 | async def on_login(self, contact: ContactSelf) -> None: 125 | print(f"用户{contact}登入") 126 | try: 127 | await contact.signature(f"签名被Wechaty更改于{datetime.now()}") 128 | except Exception as e: 129 | print("更改签名失败", e, file=sys.stderr) 130 | 131 | asyncio.run(MyBot().start()) 132 | ``` -------------------------------------------------------------------------------- /python-wechaty/docs/references/filebox.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/references/filebox.md -------------------------------------------------------------------------------- /python-wechaty/docs/references/friendship.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Friendship 3 | --- 4 | 5 | # class Friendship() 6 | > 发送、接收好友请求和好友确认事件。 7 | > [示例/Friend-Bot](https://github.com/wechaty/python-wechaty-getting-started/blob/master/examples/advanced/friendship-bot.py) 8 | 9 | 10 | ::: wechaty.user.friendship.Friendship.search 11 | 12 | ::: wechaty.user.friendship.Friendship.add 13 | ### 示例代码 14 | ```python 15 | memberList = await room.memberList() 16 | for member in memberList: 17 | await bot.Friendship.add(member, 'Nice to meet you! I am wechaty bot!') 18 | ``` 19 | 20 | ::: wechaty.user.friendship.Friendship.contact 21 | ### 示例代码 22 | ```python 23 | import asyncio 24 | from wechaty import Wechaty, Friendship 25 | 26 | 27 | class MyBot(Wechaty): 28 | 29 | async on_friendship(self, friendship: Friendship) -> None: 30 | contact = friendship.contact() 31 | await contact.ready() 32 | log_msg = f'receive "friendship" message from {contact.name}' 33 | print(log_msg) 34 | 35 | 36 | asyncio.run(MyBot().start()) 37 | ``` 38 | 39 | ::: wechaty.user.friendship.Friendship.accept 40 | ### 示例代码 41 | ```python 42 | import asyncio 43 | from wechaty import Wechaty, Friendship 44 | 45 | class MyBot(Wechaty): 46 | 47 | async on_friendship(self, friendship: Friendship) -> None: 48 | contact = friendship.contact() 49 | await contact.ready() 50 | 51 | if friendship.type() == FriendshipType.FRIENDSHIP_TYPE_RECEIVE: 52 | log_msg = 'accepted automatically' 53 | await friendship.accept() 54 | # if want to send msg, you need to delay sometimes 55 | 56 | print('waiting to send message ...') 57 | await asyncio.sleep(3) 58 | await contact.say('hello from wechaty ...') 59 | print('after accept ...') 60 | elif friendship.type() == FriendshipType.FRIENDSHIP_TYPE_CONFIRM: 61 | log_msg = 'friend ship confirmed with ' + contact.name 62 | 63 | print(log_msg) 64 | 65 | asyncio.run(MyBot().start()) 66 | ``` 67 | 68 | ::: wechaty.user.friendship.Friendship.hello 69 | ### 示例代码 70 | > 自动接受好友请求中包含消息为 `ding` 的好友请求 71 | 72 | ```python 73 | import asyncio 74 | from wechaty import Wechaty, Friendship 75 | 76 | 77 | class MyBot(Wechaty): 78 | 79 | async on_friendship(self, friendship: Friendship) -> None: 80 | contact = friendship.contact() 81 | await contact.ready() 82 | 83 | if friendship.type() == FriendshipType.FRIENDSHIP_TYPE_RECEIVE and friendship.hello() == 'ding': 84 | log_msg = 'accepted automatically because verify messsage is "ding"' 85 | await friendship.accept() 86 | # if want to send msg, you need to delay sometimes 87 | 88 | print('waiting to send message ...') 89 | await asyncio.sleep(3) 90 | await contact.say('hello from wechaty ...') 91 | print('after accept ...') 92 | 93 | asyncio.run(MyBot().start()) 94 | ``` 95 | 96 | ::: wechaty.user.friendship.Friendship.type 97 | 98 | ::: wechaty.user.friendship.Friendship.from_json -------------------------------------------------------------------------------- /python-wechaty/docs/references/index.md: -------------------------------------------------------------------------------- 1 | 2 | python-wechaty的接口非常人性化和轻量级,通过不同模块不同接口来完成定制化的功能。在这个章节中将会为你详细介绍不同模块下的接口细节。 3 | 4 | ## 模块 5 | 6 | python-wechaty中所有模块都可直接从top-level来导入,例如: 7 | 8 | ```python 9 | from wechaty import Wechaty, Message 10 | ``` 11 | 12 | ### Wechaty 类 13 | 14 | - [Class Wechaty](./wechaty) 15 | 16 | ```python 17 | from wechaty import Wechaty 18 | ``` 19 | 20 | 当开发者想要编写聊天机器人时,可通过面向函数式编程和面向对象编程两种模式: 21 | 22 | * 面向函数编程 23 | 24 | ```python 25 | import os, asyncio 26 | 27 | from wechaty import Message, Wechaty 28 | 29 | async def on_message(msg: Message): 30 | if msg.text() == 'ding': 31 | await msg.say('dong') 32 | 33 | async def main(): 34 | bot = Wechaty() 35 | bot.on('message', on_message) 36 | 37 | await bot.start() 38 | print('[Python Wechaty] Ding Dong Bot started.') 39 | 40 | asyncio.run(main()) 41 | ``` 42 | 43 | * 面向对象编程 44 | 45 | ```python 46 | import asyncio 47 | 48 | from wechaty import ( 49 | Wechaty, Contact, Message 50 | ) 51 | 52 | class MyBot(Wechaty): 53 | async def on_message(self, msg: Message): 54 | if msg.text() == 'ding': 55 | await msg.say('dong') 56 | 57 | asyncio.run(MyBot().start()) 58 | ``` 59 | 60 | 以上两种方式中,各有优劣,可是我推荐使用面向对象编程,这个在封装性和代码提示的角度上都对开发者比较友好。 61 | 62 | ### 用户相关模块 63 | 64 | 当开发者想要搜索联系人,主动给某个联系人发送消息时,此时需要主动加载联系人对象,然后发送消息。模块类型有: 65 | 66 | > 推荐:所有系统初始化相关的任务都需要在ready事件触发之后执行。 67 | 68 | - [Class Message](./message) 69 | - [Class Contact](./contact) 70 | - [Class ContactSelf](./contact-self) 71 | - [Class Room](./room) 72 | - [Class RoomInvitation](./room-invitation) 73 | - [Class Friendship](./friendship) 74 | 75 | ⚠️ 注意:在python-wechaty中加载以上模块的方式: 76 | 77 | * 面向函数式编程 78 | 79 | ```python 80 | # bot:机器人实例对象,函数内可访问的对象,推荐使用单例模式来构建 81 | contacts: List[Contact] = bot.Contact.find_all() 82 | ``` 83 | 84 | * 面向对象编程 85 | 86 | ```python 87 | async def on_ready(self, payload): 88 | # self: 机器人实例对象,而且还有良好的代码自动提示的功能 89 | contacts: List[Contact] = self.Contact.find_all() 90 | ``` 91 | -------------------------------------------------------------------------------- /python-wechaty/docs/references/room-invitation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: RoomInvitation 3 | --- 4 | 5 | 对群聊邀请事件的封装 6 | 7 | ## RoomInvitation 8 | 9 | 接受群聊的邀请 10 | 11 | **类型**: 全局类 12 | 13 | * [RoomInvitation](room-invitation.md#RoomInvitation) 14 | * [.accept\(\)](room-invitation.md#RoomInvitation+accept) ⇒ `None` 15 | * [.inviter\(\)](room-invitation.md#RoomInvitation+inviter) ⇒ `Contact` 16 | * [.topic\(\)](room-invitation.md#RoomInvitation+topic) ⇒ `str` 17 | * [~~.roomTopic\(\)~~](room-invitation.md#RoomInvitation+roomTopic) ⇒ `str` 18 | * [.date\(\)](room-invitation.md#RoomInvitation+date) ⇒ `datetime` 19 | * [.age\(\)](room-invitation.md#RoomInvitation+age) ⇒ `int` 20 | 21 | ### async def accept\(self\) ⇒ `None` 22 | 23 | 接受群聊邀请 24 | 25 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法 26 | 27 | #### 示例 28 | 29 | ```python 30 | import asyncio 31 | from wechaty import Wechaty, RoomInvitation 32 | 33 | 34 | class MyBot(Wechaty): 35 | 36 | async def on_room_invite(self, room_invitation: RoomInvitation) -> None: 37 | try: 38 | print("收到群聊邀请事件") 39 | await room_invitation.accept() 40 | print("已经自动接受") 41 | except Exception as e: 42 | print(e) 43 | 44 | asyncio.run(MyBot().start()) 45 | ``` 46 | 47 | ### async def inviter\(self\) ⇒ `Contact` 48 | 49 | 获取群聊邀请的邀请人 50 | 51 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法 52 | 53 | #### 示例 54 | 55 | ```python 56 | import asyncio 57 | from wechaty import Wechaty, RoomInvitation 58 | 59 | 60 | class MyBot(Wechaty): 61 | 62 | async def on_room_invite(self, room_invitation: RoomInvitation) -> None: 63 | try: 64 | print("收到群聊邀请事件") 65 | inviter = await room_invitation.inviter() 66 | inviter_name = inviter.name 67 | print(f"收到来自{inviter_name}的群聊邀请") 68 | except Exception as e: 69 | print(e) 70 | 71 | asyncio.run(MyBot().start()) 72 | ``` 73 | 74 | ### async def topic\(self\) ⇒ `str` 75 | 76 | 获取群聊邀请的群聊名 77 | 78 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法 79 | 80 | #### 示例 81 | 82 | ```python 83 | import asyncio 84 | from wechaty import Wechaty, RoomInvitation 85 | 86 | 87 | class MyBot(Wechaty): 88 | 89 | async def on_room_invite(self, room_invitation: RoomInvitation) -> None: 90 | try: 91 | room_name = await room_invitation.topic() 92 | print(f"收到来自{room_name}的群聊邀请") 93 | except Exception as e: 94 | print(e) 95 | 96 | asyncio.run(MyBot().start()) 97 | ``` 98 | 99 | ### ~~async def roomTopic\(\)~~ 100 | 101 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法 102 | **已弃用:**: 请使用 topic\(\) 103 | 104 | ### async def date\(self\) ⇒ `datetime` 105 | 106 | 获取群聊邀请的日期 107 | 108 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法 109 | 110 | ### async def age\(self\) ⇒ `int` 111 | 112 | 获取当前距离已接收到的这条群聊邀请的时间的间隔, 单位为秒 113 | 114 | 举个例子, 有条群聊邀请是`8:43:01`发送的, 而当我们在Wechaty中接收到它的时候时间已经为 `8:43:15`, 那么这时 `age()`返回的值为 `8:43:15 - 8:43:01 = 14 (秒)` 115 | 116 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法 117 | -------------------------------------------------------------------------------- /python-wechaty/docs/references/wechaty.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Wechaty 3 | --- 4 | 5 | > `Wechaty`类用来实例化机器人对象,控制机器人的整体逻辑,如:启动、注册监听事件、登录、注销等功能。 6 | ------ 7 | > 一个机器人就是`Wechaty`实例,所有用户相关模块都应该通过实例来获取,这样能保证服务连接的一致性,此外所有的逻辑应该以插件和事件订阅的形式组织,保证不同业务之间的隔离性以及业务内的内聚性。 8 | 9 | ::: wechaty.Wechaty.__init__ 10 | ### 示例代码 11 | (世界上最短的Python ChatBot:9行代码) 12 | ```python 13 | from wechaty import Wechaty 14 | import asyncio 15 | async def main(): 16 | bot = Wechaty() 17 | bot.on('scan', lambda status, qrcode, data: print('Scan QR Code to login: {}\nhttps://wechaty.js.org/qrcode/{}'.format(status, qrcode))) 18 | bot.on('login', lambda user: print('User {} logged in'.format(user))) 19 | bot.on('message', lambda message: print('Message: {}'.format(message))) 20 | await bot.start() 21 | asyncio.run(main()) 22 | ``` 23 | ### WechatyOptions 24 | 25 | 创建一个wechaty实例的可选参数 26 | 27 | **Kind**: global typedef 28 | **Properties** 29 | 30 | | Name | Type | Description | 31 | | :--- | :--- | :--- | 32 | | profile | `string` | Wechaty Name. When you set this: `new Wechaty({profile: 'wechatyName'})` it will generate a file called `wechatyName.memory-card.json`. This file stores the bot's login information. If the file is valid, the bot can auto login so you don't need to scan the qrcode to login again. Also, you can set the environment variable for `WECHATY_PROFILE` to set this value when you start. eg: `WECHATY_PROFILE="your-cute-bot-name" node bot.js`. This field is deprecated, please use `name` instead. [see more](https://github.com/wechaty/wechaty/issues/2049) | 33 | | puppet | `PuppetModuleName` \| `Puppet` | Puppet name or instance | 34 | | puppetOptions | `Partial.` | Puppet TOKEN | 35 | | ioToken | `string` | Io TOKEN | 36 | 37 | ::: wechaty.Wechaty.instance 38 | 39 | ::: wechaty.Wechaty.use 40 | 41 | ::: wechaty.Wechaty.on 42 | ### WechatyEventName 43 | 44 | Wechaty类的事件类型 45 | 46 | **Kind**: global typedef 47 | **Properties** 48 | 49 | | Name | Type | Description | 50 | | :--- | :--- | :--- | 51 | | error | `string` | When the bot get error, there will be a Wechaty error event fired. | 52 | | login | `string` | After the bot login full successful, the event login will be emitted, with a Contact of current logined user. | 53 | | logout | `string` | Logout will be emitted when bot detected log out, with a Contact of the current login user. | 54 | | heartbeat | `string` | Get bot's heartbeat. | 55 | | friendship | `string` | When someone sends you a friend request, there will be a Wechaty friendship event fired. | 56 | | message | `string` | Emit when there's a new message. | 57 | | ready | `string` | Emit when all data has load completed, in wechaty-puppet-padchat, it means it has sync Contact and Room completed | 58 | | room-join | `string` | Emit when anyone join any room. | 59 | | room-topic | `string` | Get topic event, emitted when someone change room topic. | 60 | | room-leave | `string` | Emit when anyone leave the room. | 61 | | room-invite | `string` | Emit when there is a room invitation, see more in [RoomInvitation](room-invitation.md) If someone leaves the room by themselves, wechat will not notice other people in the room, so the bot will never get the "leave" event. | 62 | | scan | `string` | A scan event will be emitted when the bot needs to show you a QR Code for scanning. </br> It is recommend to install qrcode-terminal\(run `npm install qrcode-terminal`\) in order to show qrcode in the terminal. | 63 | 64 | ### WechatyEventFunction 65 | 66 | Wechaty类的事件所绑定的相关函数 67 | 68 | **Kind**: global typedef 69 | **Properties** 70 | 71 | | Name | Type | Description | 72 | | :--- | :--- | :--- | 73 | | error | `function` | \(this: Wechaty, error: Error\) => void callback function | 74 | | login | `function` | \(this: Wechaty, user: ContactSelf\)=> void | 75 | | logout | `function` | \(this: Wechaty, user: ContactSelf\) => void | 76 | | scan | `function` | \(this: Wechaty, url: string, code: number\) => void | 77 | | heartbeat | `function` | \(this: Wechaty, data: any\) => void | 78 | | friendship | `function` | \(this: Wechaty, friendship: Friendship\) => void | 79 | | message | `function` | \(this: Wechaty, message: Message\) => void | 80 | | ready | `function` | \(this: Wechaty\) => void | 81 | | room-join | `function` | \(this: Wechaty, room: Room, inviteeList: Contact\[\], inviter: Contact\) => void | 82 | | room-topic | `function` | \(this: Wechaty, room: Room, newTopic: string, oldTopic: string, changer: Contact\) => void | 83 | | room-leave | `function` | \(this: Wechaty, room: Room, leaverList: Contact\[\]\) => void | 84 | | room-invite | `function` | \(this: Wechaty, room: Room, leaverList: Contact\[\]\) => void see more in [RoomInvitation](room-invitation.md) | 85 | 86 | ::: wechaty.Wechaty.start 87 | ### 示例代码 88 | ```python 89 | from wechaty import Wechaty 90 | import asyncio 91 | 92 | async def main(): 93 | bot = Wechaty() 94 | await bot.start() 95 | 96 | asyncio.run(main()) 97 | ``` 98 | 99 | ::: wechaty.Wechaty.restart 100 | 101 | ::: wechaty.Wechaty.stop 102 | 103 | ::: wechaty.Wechaty.user_self 104 | 105 | ::: wechaty.Wechaty.self -------------------------------------------------------------------------------- /python-wechaty/docs/tutorials/getting-started.md: -------------------------------------------------------------------------------- 1 | ## 快速开始 2 | 3 | 1、 安装 4 | 5 | ```shell 6 | pip install --upgrade wechaty 7 | ``` 8 | 9 | 2、 设置TOKEN 10 | 11 | ```shell 12 | export token=your_token_at_here 13 | # or 14 | export WECHATY_PUPPET_SERVICE_TOKEN=your_token_at_here 15 | ``` 16 | 17 | 或者通过代码来设置环境变量: 18 | 19 | ```python 20 | import os 21 | os.environ['token'] = 'your_token_at_here' 22 | # or 23 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN'] = 'your_token_at_here' 24 | ``` 25 | 26 | 3、 聊天机器人 27 | 28 | ```python 29 | import asyncio 30 | 31 | from wechaty import Wechaty 32 | 33 | class MyBot(Wechaty): 34 | async def on_message(self, msg: Message): 35 | from_contact = msg.talker() 36 | text = msg.text() 37 | room = msg.room() 38 | if text == 'ding': 39 | conversation: Union[ 40 | Room, Contact] = from_contact if room is None else room 41 | await conversation.ready() 42 | await conversation.say('dong') 43 | file_box = FileBox.from_url( 44 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 45 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 46 | name='ding-dong.jpg') 47 | await conversation.say(file_box) 48 | 49 | asyncio.run(MyBot().start()) 50 | ``` 51 | 52 | 以上代码展示了基于python-wechaty如何开发聊天机器人的整体步骤:安装、设置TOKEN环境变量以及编写聊天机器人。示例机器人代码可查看:[ding-dong-bot-oop.py](https://github.com/wechaty/python-wechaty-getting-started/blob/master/examples/basic/ding-dong-bot-oop.py) 53 | 54 | ## 快速上手 55 | 56 | - [使用padlocal协议](./use_padlocal_getting_started.md) 57 | - [使用web协议](./use_web_getting_started.md) 58 | - [使用Paimon协议](./use_paimon_getting_started.md) 59 | -------------------------------------------------------------------------------- /python-wechaty/docs/tutorials/index.md: -------------------------------------------------------------------------------- 1 | ## 快速开始一个简单的应用 2 | ### 本节内容中 将手把手快速指导你开始运行一个消息机器人。 3 | ### 暂时还只有微信机器人相关的教程,后面可以补足: 微信公众号,钉钉,飞书,Whatsapp,Gitter这几种 4 | 5 | ### 运行一个微信机器人 6 | 运行微信机器人有多种协议可以使用,有以下三种不同的协议方式, 每个协议都有不用的特点,如果你不知道选什么,就从Padlocal协议开始吧。 7 | > 此处最好放上几种协议的对比,以及具体的各自如何快速上手的的跳转链接 8 | - 使用Padlocal协议(推荐使用) 9 | - 使用Paimon协议 10 | - 使用免费Web协议 11 | 12 | ### 运行一个微信公众号机器人 13 | 14 | ### 运行一个钉钉机器人 15 | 16 | ### 运行一个Whatsapp机器人 17 | 18 | ### 运行一个Gitter机器人 -------------------------------------------------------------------------------- /python-wechaty/docs/tutorials/use_padlocal_getting_started.md: -------------------------------------------------------------------------------- 1 | # 使用Padlocal协议启动微信机器人 2 | 3 | 底层的对接实现是基于TypeScript语言,故无法直接在python-wechaty中使用该服务。可是Wechaty社区能够直接将其转化成对应的服务让多语言调用,从而实现:底层复用的特性。 4 | 5 | 整体步骤分为三步: 6 | 7 | * 申请一个TOKEN 8 | * 使用Docker启动Padlocal网关服务 9 | * 使用python-wechaty连接服务并启动启动微信机器人 10 | 11 | 12 | ## 一、申请一个TOKEN 13 | - 可以通过手机号注册来获得一个7天免费的TOKEN:[申请地址](http://pad-local.com) 14 | - [TOKEN 说明](https://wechaty.js.org/docs/puppet-services/) 15 | - 那如何获取长期Token呢?详细请看:[Everything-about-Wechaty](https://github.com/juzibot/Welcome/wiki/Everything-about-Wech aty) 16 | 17 | 18 | ## 二、使用Docker启动Padlocal网关服务 19 | - 这一步可以在本机运行也可以在服务器上运行。 20 | - 如果在服务器端运行,则须注意服务器相应端口防火墙规则需要打开 21 | - 如果是在本地测试运行,则需要在启动机器人时指定环境变量SERVICE_ENDPOINT 22 | 23 | ### 2.1 脚本 24 | 25 | ```shell 26 | # 设置环境变量 27 | 28 | export WECHATY_LOG="verbose" 29 | export WECHATY_PUPPET="wechaty-puppet-padlocal" 30 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_XXXXXX" 31 | 32 | export WECHATY_PUPPET_SERVER_PORT="9001" 33 | # 可使用代码随机生成UUID:import uuid;print(uuid.uuid4()); 34 | export WECHATY_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX" 35 | 36 | docker run -ti \ 37 | --name wechaty_puppet_service_token_gateway \ 38 | --rm \ 39 | -e WECHATY_LOG \ 40 | -e WECHATY_PUPPET \ 41 | -e WECHATY_PUPPET_PADLOCAL_TOKEN \ 42 | -e WECHATY_PUPPET_SERVER_PORT \ 43 | -e WECHATY_TOKEN \ 44 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \ 45 | wechaty/wechaty:0.65 46 | ``` 47 | 48 | > 在此我默认所有的人都对[Docker](https://www.docker.com)的基本使用已经有了一定的了解,否则可以花几分钟去看看其[文档](https://www.docker.com/get-started)熟悉一下。 49 | 50 | ### 2.2 参数说明 51 | 52 | * **WECHATY_PUPPET**: **标识**使用的哪个协议,一般和`token`类型的一一对应。比如当使用`padlocal`协议的话,那这个就是`wechaty-puppet-padlocal`,如果使用`web`协议的话,那这个就是`wechaty-puppet-wechat`。 53 | * **WECHATY_PUPPET_PADLOCAL_TOKEN**: 这个协议是用来连接Padlocal的服务,目前是付费的。也就是在第一步中申请的。 54 | * **WECHATY_PUPPET_SERVER_PORT**: 网关服务的接口,提供给`python-wechaty`来连接调用,如果服务部署在云服务器上,则需要保证该端口的可访问性。 55 | * **WECHATY_TOKEN**: 当开发者在自己机器上启动一个网关服务时,需要通过`TOEKN`来做身份验证,避免服务被他人窃取。 56 | 57 | 以上代码只需要修改三个参数:`WECHATY_PUPPET_PADLOCAL_TOKEN`, `WECHATY_PUPPET_SERVER_PORT`, `WECHATY_TOKEN` 即可成功启动Token网关服务。 58 | 59 | 那网关服务启动成功之后,只需要编写`python-wechaty`的代码来连接即可。 60 | 61 | 62 | 63 | ## 三、使用python-wechaty连接服 64 | 65 | ### 3.1 本地测试和远端部署 66 | 67 | 当启动网关服务时,`Padlocal`会根据`WECHATY_TOKEN`来在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上注册部署机器的`IP`和`端口`,然后python-wechaty会根据`WECHATY_TOKEN`在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上获取对应的IP和端口。 68 | 69 | 可是很多小伙伴在实际开发的过程中,通常会出现`endpoint is not invalid`等错误信息,那是因为开发者有可能在本地启动网关服务或者服务器端口没有开放。 70 | 71 | 网关服务的部署通常是分为本地测试和远端部署,前者通常只是为了初学测试,后者是为了生产部署。如果是在生产部署时,只需要设置环境变量: 72 | 73 | ```shell 74 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 75 | # or 76 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 77 | # or 78 | export token=1fe5f846-3cfb-401d-b20c-XXXXX 79 | ``` 80 | 81 | 可是如果是在本地测试时,则通过ENDPOINT来找到启动的网关服务。 82 | 83 | ```shell 84 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 85 | # or 86 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX 87 | # or 88 | export token=1fe5f846-3cfb-401d-b20c-XXXXX 89 | 90 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:9001 91 | # or 92 | export ENDPOINT=127.0.0.1:9001 93 | # or 94 | export endpoint=127.0.0.1:9001 95 | ``` 96 | 97 | ### 3.2 TOKEN的作用 98 | 99 | 总而言之: 100 | 101 | * 如果是公网环境下,可只需要设置`TOKEN`即可(因为你的token已经注册在chatie server上,故可以获取到目标资源服务器的ip和port) 102 | * 如果是内网环境下,可只需要使用`ENDPOINT`(`localhost:port`)来让python-wechaty连接目标资源服务器。 103 | 104 | > 如果是token是padlocal类型,则在python-wechaty程序内部可直接设置`export endpoint=localhost:port`来连接Gateway Server。 105 | 106 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看: 107 | 108 | ```python 109 | import os 110 | os.environ['token'] = "1fe5f846-3cfb-401d-b20c-XXXXX" 111 | os.environ['endpoint'] = "127.0.0.1:9001" 112 | ``` 113 | 114 | ### 3.3 机器人启动代码 115 | 116 | > talke is cheep, show you the code 117 | 118 | ```python 119 | import asyncio, os 120 | from typing import List, Optional, Union 121 | 122 | from wechaty_puppet import FileBox # type: ignore 123 | 124 | from wechaty import Wechaty, Contact 125 | from wechaty.user import Message, Room 126 | 127 | 128 | class MyBot(Wechaty): 129 | 130 | async def on_message(self, msg: Message): 131 | """ 132 | listen for message event 133 | """ 134 | from_contact: Optional[Contact] = msg.talker() 135 | text = msg.text() 136 | room: Optional[Room] = msg.room() 137 | if text == 'ding': 138 | conversation: Union[ 139 | Room, Contact] = from_contact if room is None else room 140 | await conversation.ready() 141 | await conversation.say('dong') 142 | file_box = FileBox.from_url( 143 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 144 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 145 | name='ding-dong.jpg') 146 | await conversation.say(file_box) 147 | 148 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX" 149 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = "127.0.0.1:9001" 150 | asyncio.run(MyBot().start()) 151 | ``` 152 | ### 运行代码 153 | 154 | 155 | 欢迎各位品尝以上代码 🥳 156 | 157 | * **相关链接** 158 | * [python-wechaty](https://github.com/wechaty/python-wechaty) 159 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started) -------------------------------------------------------------------------------- /python-wechaty/docs/tutorials/use_paimon_getting_started.md: -------------------------------------------------------------------------------- 1 | # 使用Paimon协议 2 | 3 | ## 一、介绍 4 | 5 | python原生支持paimon协议,不需要Token Gateway,简单方便。 6 | [免费申请7天试用Token](https://wechaty.js.org/docs/puppet-services/paimon) 7 | 8 | 9 | ## 二、连接服务 10 | 11 | ### 2.1 本地测试和远端部署 12 | 13 | 14 | ```shell 15 | export WECHATY_PUPPET_SERVICE_TOKEN=puppet_paimon_XXXXX 16 | # or 17 | export TOKEN=puppet_paimon_XXXXX 18 | # or 19 | export token=puppet_paimon_XXXXX 20 | ``` 21 | 22 | 23 | 24 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看: 25 | 26 | ```python 27 | import os 28 | os.environ['token'] = "puppet_paimon_XXXXX" 29 | ``` 30 | 31 | ## 三、示例代码 32 | 33 | > talke is cheep, show you the code 34 | 35 | ```python 36 | import asyncio, os 37 | from typing import List, Optional, Union 38 | 39 | from wechaty_puppet import FileBox # type: ignore 40 | 41 | from wechaty import Wechaty, Contact 42 | from wechaty.user import Message, Room 43 | 44 | 45 | class MyBot(Wechaty): 46 | 47 | async def on_message(self, msg: Message): 48 | """ 49 | listen for message event 50 | """ 51 | from_contact: Optional[Contact] = msg.talker() 52 | text = msg.text() 53 | room: Optional[Room] = msg.room() 54 | if text == 'ding': 55 | conversation: Union[ 56 | Room, Contact] = from_contact if room is None else room 57 | await conversation.ready() 58 | await conversation.say('dong') 59 | file_box = FileBox.from_url( 60 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 61 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 62 | name='ding-dong.jpg') 63 | await conversation.say(file_box) 64 | 65 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX" 66 | asyncio.run(MyBot().start()) 67 | ``` 68 | 69 | 欢迎各位品尝以上代码 🥳 70 | 71 | * **相关链接** 72 | * [python-wechaty](https://github.com/wechaty/python-wechaty) 73 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started) 74 | -------------------------------------------------------------------------------- /python-wechaty/docs/tutorials/use_web_getting_started.md: -------------------------------------------------------------------------------- 1 | # 使用免费Web协议 2 | 3 | ## 一、介绍 4 | 5 | 底层的对接实现是基于TypeScript语言,故无法直接在python-wechaty中使用该服务。可是Wechaty社区能够直接将其转化成对应的服务让多语言调用,从而实现:底层复用的特性。 6 | 7 | 整体步骤分为两步: 8 | 9 | * 使用Docker启动web协议服务 10 | * 使用python-wechaty连接服务 11 | 12 | ## 二、启动Web协议服务 13 | 14 | ```shell 15 | docker pull wechaty/wechaty:0.65 16 | 17 | export WECHATY_LOG="verbose" 18 | export WECHATY_PUPPET="wechaty-puppet-wechat" 19 | export WECHATY_PUPPET_SERVER_PORT="8080" 20 | export WECHATY_TOKEN="python-wechaty-{uuid}" 21 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true" 22 | 23 | # save login session 24 | if [ ! -f "${WECHATY_TOKEN}.memory-card.json" ]; then 25 | touch "${WECHATY_TOKEN}.memory-card.json" 26 | fi 27 | 28 | docker run -ti \ 29 | --name wechaty_puppet_service_token_gateway \ 30 | --rm \ 31 | -v "`pwd`/${WECHATY_TOKEN}.memory-card.json":"/wechaty/${WECHATY_TOKEN}.memory-card.json" \ 32 | -e WECHATY_LOG \ 33 | -e WECHATY_PUPPET \ 34 | -e WECHATY_PUPPET_SERVER_PORT \ 35 | -e WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER \ 36 | -e WECHATY_TOKEN \ 37 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \ 38 | wechaty/wechaty:0.65 39 | ``` 40 | 41 | 注意: 42 | 43 | * WECHATY_TOKEN 必须使用生成的UUID来替换,不然直接使用该token来启动的服务很容易被他人盗窃。 44 | 45 | 小伙伴们可在python解释器中运行以下代码来获得随机TOKEN: 46 | ```python 47 | # 例如:b2ff8fc5-c5a2-4384-b317-3695807e483f 48 | import uuid;print(uuid.uuid4()); 49 | ``` 50 | 51 | ## 三、连接服务 52 | 53 | 当使用docker来启动web服务时,可分为在本地环境测试以及在远端环境中测试,在连接方式上有一些不一样。 54 | 55 | ### 3.1 本地WEB服务 56 | 57 | 当在计算机本地启动web服务后,可直接使用python-wechaty连接本地的服务,不通过token来获取对应的服务连接地址。示例代码如下: 58 | 59 | ```shell 60 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080 61 | ``` 62 | 63 | 或者 64 | 65 | ```python 66 | import os 67 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '127.0.0.1:8080' 68 | ``` 69 | 70 | > 当你的服务和python-wechaty机器人代码都部署在服务器中时,此时也属于本地服务,可使用此方法来配置。 71 | 72 | ### 3.2 远端服务 73 | 74 | 当把服务部署在远端服务器中时,要保证该计算机能够被外网访问到,且对应端口开放。例如在上述示例脚本中,比如保证服务器的`8080`端口开放,而你的服务器IP为:`10.12.123.23`,此时可直接设置服务连接地址: 75 | 76 | ```shell 77 | export WECHATY_PUPPET_SERVICE_ENDPOINT=10.12.123.23:8080 78 | ``` 79 | 80 | 或者 81 | 82 | ```python 83 | import os 84 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '10.12.123.23:8080' 85 | ``` 86 | 87 | ## 四、编写代码 88 | 89 | > talk is cheep, show you the code 90 | 91 | ```python 92 | import asyncio 93 | from typing import List, Optional, Union 94 | 95 | from wechaty_puppet import FileBox # type: ignore 96 | 97 | from wechaty import Wechaty, Contact 98 | from wechaty.user import Message, Room 99 | 100 | 101 | class MyBot(Wechaty): 102 | 103 | async def on_message(self, msg: Message): 104 | """ 105 | listen for message event 106 | """ 107 | from_contact: Optional[Contact] = msg.talker() 108 | text = msg.text() 109 | room: Optional[Room] = msg.room() 110 | if text == 'ding': 111 | conversation: Union[ 112 | Room, Contact] = from_contact if room is None else room 113 | await conversation.ready() 114 | await conversation.say('dong') 115 | file_box = FileBox.from_url( 116 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 117 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 118 | name='ding-dong.jpg') 119 | await conversation.say(file_box) 120 | 121 | asyncio.run(MyBot().start()) 122 | ``` 123 | 124 | 欢迎各位品尝以上代码 🥳 125 | -------------------------------------------------------------------------------- /python-wechaty/docs/tutorials/videos.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 介绍视频 3 | summary: . 4 | authors: 5 | - wj-Mcat 6 | date: 2021-04-25 7 | some_url: https://github.com/wj-Mcat 8 | --- 9 | 10 | ## live-coding 11 | 12 | > 原始视频链接:[@吴京京 python-wechaty-live-coding](https://www.bilibili.com/video/BV1dv411k75G?from=search&seid=13874361539953942172) 13 | 14 | 15 | 16 | ## AI情话 17 | 18 | > 原始视频链接:[@Val_傲娇的鸽子 手把手教你做个用AI续写情话的Wechaty聊天机器人](https://www.bilibili.com/video/BV1BB4y1A714?from=search&seid=13874361539953942172) 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /python-wechaty/examples/contact-bot.py: -------------------------------------------------------------------------------- 1 | """doc""" 2 | import asyncio 3 | from typing import Optional 4 | 5 | from wechaty_puppet import ContactType 6 | from wechaty_puppet.logger import get_logger 7 | 8 | from wechaty import Wechaty, Contact 9 | from wechaty.utils.qrcode_terminal import qr_terminal_str 10 | 11 | log = get_logger('ContactBot') 12 | 13 | WELCOME_MSG = ''' 14 | =============== Powered by Wechaty =============== 15 | -------- https://github.com/Chatie/wechaty -------- 16 | Hello, 17 | I'm a Wechaty Botie with the following super powers: 18 | 1. List all your contacts with weixn id & name 19 | 2. Dump the avatars of your first 17 contacts 20 | __________________________________________________ 21 | Hope you like it, and you are very welcome to 22 | upgrade me for more super powers! 23 | Please wait... I'm trying to login in... 24 | ''' 25 | 26 | # pylint: disable=W0602 27 | bot: Optional[Wechaty] = None 28 | MAX_CONTACTS = 17 29 | INTERVAL = 7 30 | 31 | 32 | async def display_contacts() -> None: 33 | """Display all the contacts and dump the avatars""" 34 | # pylint: disable=W0603 35 | global bot 36 | assert bot is not None 37 | while True: 38 | contacts = await bot.Contact.find_all() 39 | 40 | log.info('#######################') 41 | log.info('Contact number: %s\n', len(contacts)) 42 | 43 | for index, contact in enumerate(contacts): 44 | if contact.type() == ContactType.CONTACT_TYPE_PERSONAL: 45 | log.info('personal %s: %s : %s', index, contact.name, contact.get_id()) 46 | 47 | for contact in contacts[:MAX_CONTACTS]: 48 | file = await contact.avatar() 49 | name = file.name 50 | await file.to_file(name, True) 51 | 52 | log.info('Contact: "%s" with avatar file: "%s"', contact.name, name) 53 | 54 | if len(contacts) > MAX_CONTACTS: 55 | log.info('Too many contacts. I only show you the first %s ones...', MAX_CONTACTS) 56 | 57 | log.info('I will re-dump contact weixin id & names after %s second... ', INTERVAL) 58 | await asyncio.sleep(INTERVAL) 59 | 60 | 61 | async def handle_login(user: Contact) -> None: 62 | """Handle the login event""" 63 | log.info('%s logged in', user.name) 64 | await user.say('wechaty contact-bot just logged in') 65 | await display_contacts() 66 | 67 | 68 | async def main() -> None: 69 | """The main function for the contact-bot module""" 70 | # pylint: disable=W0603 71 | global bot 72 | print(WELCOME_MSG) 73 | bot = Wechaty()\ 74 | .on('login', handle_login)\ 75 | .on('error', lambda error: log.info('error: %s', error))\ 76 | .on('scan', 77 | lambda qrcode, status: print(f'{qr_terminal_str(qrcode)}\n' 78 | f'[{status}] Scan QR Code in above url to login:')) 79 | await bot.start() 80 | 81 | 82 | asyncio.run(main()) 83 | -------------------------------------------------------------------------------- /python-wechaty/examples/ding-dong-bot.py: -------------------------------------------------------------------------------- 1 | """doc""" 2 | import asyncio 3 | import logging 4 | from typing import Optional, Union 5 | 6 | from wechaty_puppet import FileBox 7 | 8 | from wechaty import Wechaty, Contact 9 | from wechaty.user import Message, Room 10 | 11 | logging.basicConfig( 12 | level=logging.INFO, 13 | format='%(asctime)s %(levelname)s %(filename)s <%(funcName)s> %(message)s', 14 | datefmt='%Y-%m-%d %H:%M:%S', 15 | ) 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | async def message(msg: Message) -> None: 21 | """back on message""" 22 | from_contact = msg.talker() 23 | text = msg.text() 24 | room = msg.room() 25 | if text == 'ding': 26 | conversation: Union[ 27 | Room, Contact] = from_contact if room is None else room 28 | await conversation.ready() 29 | await conversation.say('dong') 30 | file_box = FileBox.from_url( 31 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 32 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 33 | name='ding-dong.jpg') 34 | await conversation.say(file_box) 35 | 36 | bot: Optional[Wechaty] = None 37 | 38 | 39 | async def main() -> None: 40 | """doc""" 41 | # pylint: disable=W0603 42 | global bot 43 | bot = Wechaty().on('message', message) 44 | await bot.start() 45 | 46 | 47 | asyncio.run(main()) 48 | -------------------------------------------------------------------------------- /python-wechaty/examples/health_check_plugin.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/examples/health_check_plugin.py -------------------------------------------------------------------------------- /python-wechaty/examples/plugin-server-bot.py: -------------------------------------------------------------------------------- 1 | """doc""" 2 | import asyncio 3 | import logging 4 | from typing import Optional, Union 5 | from quart import Quart 6 | 7 | from wechaty_puppet import FileBox, PuppetOptions 8 | 9 | from wechaty import Wechaty, Contact, WechatyPlugin, WechatyOptions 10 | from wechaty.user import Message, Room 11 | 12 | logging.basicConfig( 13 | level=logging.INFO, 14 | format='%(asctime)s %(levelname)s %(filename)s <%(funcName)s> %(message)s', 15 | datefmt='%Y-%m-%d %H:%M:%S', 16 | ) 17 | 18 | log = logging.getLogger(__name__) 19 | 20 | 21 | class SimpleServerWechatyPlugin(WechatyPlugin): 22 | """ 23 | simple hello wechaty web server plugin 24 | """ 25 | async def blueprint(self, app: Quart) -> None: 26 | @app.route('/wechaty') 27 | def hello_wechaty() -> str: 28 | """helo blueprint function""" 29 | return 'hello wechaty' 30 | 31 | 32 | async def message(msg: Message) -> None: 33 | """back on message""" 34 | from_contact = msg.talker() 35 | text = msg.text() 36 | room = msg.room() 37 | if text == '#ding': 38 | conversation: Union[ 39 | Room, Contact] = from_contact if room is None else room 40 | await conversation.ready() 41 | await conversation.say('dong') 42 | file_box = FileBox.from_url( 43 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 44 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 45 | name='ding-dong.jpg') 46 | await conversation.say(file_box) 47 | 48 | bot: Optional[Wechaty] = None 49 | 50 | 51 | async def main() -> None: 52 | """doc""" 53 | # pylint: disable=W0603 54 | global bot 55 | options = WechatyOptions( 56 | host='127.0.0.1', 57 | port=5005, 58 | puppet_options=PuppetOptions( 59 | token='your-token' 60 | ) 61 | ) 62 | 63 | bot = Wechaty( 64 | options=options 65 | ).on('message', message) 66 | bot.use(SimpleServerWechatyPlugin()) 67 | 68 | await bot.start() 69 | 70 | 71 | asyncio.run(main()) 72 | -------------------------------------------------------------------------------- /python-wechaty/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: python-wechaty 2 | repo_url: http://github.com/wechaty/python-wechaty 3 | repo_name: GitHub 4 | site_description: 'python-wechaty AI Chatbot' 5 | copyright: '@wechaty community' 6 | nav: 7 | - 介绍: 'index.md' 8 | - 快速开始: 9 | - 介绍: 'tutorials/index.md' 10 | - 使用Padlocal协议快速开始一个微信机器人: 'tutorials/use_padlocal_getting_started.md' 11 | - 使用免费web协议快速开始一个微信机器人: 'tutorials/use_web_getting_started.md' 12 | - 使用paimon协议快速开始一个微信机器人: 'tutorials/use_paimon_getting_started.md' 13 | - 视频教程: 'tutorials/videos.md' 14 | - 使用指南: 15 | - 介绍: 'how-to/how-to_introduction.md' 16 | - 如何添加好友: 'how-to/how-to_add_friendship.md' 17 | - 如何关键字入群: 'how-to/how-to_room_inviter.md' 18 | - 如何完成自动回复: 'how-to/how-to_auto_reply.md' 19 | - 如何检索群聊或联系人: 'how-to/how-to_finder.md' 20 | - 如何完成任务调度: 'how-to/how-to_scheduler.md' 21 | - 如何完成群消息同步: 'how-to/how-to_message_forward.md' 22 | - 如何使用Rasa Sever: 'how-to/how-to_rasa.md' 23 | - 如何使用Github Webhook插件: 'how-to/how-to_github_webhook.md' 24 | - 如何使用Gitlab Webhook插件: 'how-to/how-to_gitlab_webhook.md' 25 | - 如何使用插件系统: 'how-to/how-to_use_plugin.md' 26 | - 模块详解: 27 | - 介绍: 'references/index.md' 28 | - 'Wechaty模块': 'references/wechaty.md' 29 | - '消息模块': 'references/message.md' 30 | - '联系人模块': 'references/contact.md' 31 | - '登录人模块': 'references/contact-self.md' 32 | - '好友关系模块': 'references/friendship.md' 33 | - '群聊模块': 'references/room.md' 34 | - '群聊邀请模块': 'references/room-invitation.md' 35 | - 'filebox模块': 'references/filebox.md' 36 | - API文档: 37 | - 'wechaty.Wechaty': 'api/wechaty.md' 38 | - 'wechaty.accessory': 'api/accessory.md' 39 | - 'wechaty.config': 'api/config.md' 40 | - 'wechaty.types': 'api/types.md' 41 | - 'wechaty.plugin': 'api/plugin.md' 42 | - wechaty.utils: 43 | - 'wechaty.utils.date_util': 'api/utils/date_util.md' 44 | - 'wechaty.utils.async_helper': 'api/utils/async_helper.md' 45 | - 'wechaty.utils.link': 'api/utils/link.md' 46 | - 'wechaty.utils.qr_code': 'api/utils/qr_code.md' 47 | - 'wechaty.utils.qrcode_teminal': 'api/utils/qrcode_terminal.md' 48 | - 'wechaty.utils.type_check': 'api/utils/type_check.md' 49 | - wechaty.user: 50 | - 'wechaty.user.contact': 'api/user/contact.md' 51 | - 'wechaty.user.contact_self': 'api/user/contact_self.md' 52 | - 'wechaty.user.favorite': 'api/user/favorite.md' 53 | - 'wechaty.user.friendship': 'api/user/friendship.md' 54 | - 'wechaty.user.image': 'api/user/image.md' 55 | - 'wechaty.user.message': 'api/user/message.md' 56 | - 'wechaty.user.mini_program': 'api/user/mini_program.md' 57 | - 'wechaty.user.room': 'api/user/room.md' 58 | - 'wechaty.user.room_invitation': 'api/user/room_invitation.md' 59 | - 'wechaty.user.tag': 'api/user/tag.md' 60 | - 'wechaty.user.url_link': 'api/user/url_link.md' 61 | - 设计理念: 62 | - 介绍: 'explanation/index.md' 63 | - 不同协议比较: 'explanation/different_protocol.md' 64 | - 插件系统: 'explanation/why_plugin.md' 65 | - FAQ: 66 | - '基础常见问题': 'faq/common.md' 67 | - '什么是Puppet': 'faq/what-is-a-puppet.md' 68 | 69 | theme: 70 | name: material 71 | logo: img/wechaty-icon-white.svg 72 | favicon: img/favicon.ico 73 | 74 | markdown_extensions: 75 | - pymdownx.highlight 76 | - pymdownx.superfences 77 | - toc: 78 | baselevel: 2 79 | 80 | google_analytics: 81 | - G-1TDFTF2BYD 82 | - auto 83 | 84 | plugins: 85 | - search 86 | - mkdocstrings: 87 | handlers: 88 | python: 89 | selection: 90 | filters: 91 | - "!^_" # exlude all members starting with _ 92 | - "^__init__$" # but always include __init__ modules and methods 93 | rendering: 94 | show_root_heading: yes 95 | show_root_full_path: false 96 | members_order: source 97 | heading_level: 2 98 | watch: 99 | - ./src -------------------------------------------------------------------------------- /python-wechaty/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.mypy] 2 | disallow_untyped_defs = true 3 | warn_unused_ignores = true 4 | ignore_missing_imports = false 5 | 6 | [[tool.mypy.overrides]] 7 | module = [ 8 | "qrcode.*", 9 | "wechaty_puppet.*", 10 | "pytest.*", 11 | "grpclib.*", 12 | "lxml.*", 13 | "apscheduler.*", 14 | "pyee.*" 15 | ] 16 | ignore_missing_imports = true 17 | 18 | # refer to: https://docs.pytest.org/en/stable/mark.html 19 | [tool.pytest.ini_options] 20 | minversion = "6.0" 21 | addopts = "--cov-report term-missing --cov-report xml --cov=src/" 22 | testpaths = [ 23 | "tests", 24 | ] 25 | markers = [ 26 | "asyncio" 27 | ] 28 | pythonpath='./src' -------------------------------------------------------------------------------- /python-wechaty/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # use the latest version of setuptools 2 | setuptools>=63.2.0 3 | flake8 4 | mypy 5 | mypy_extensions 6 | pycodestyle 7 | pylint 8 | pylint-quotes 9 | pytest 10 | 11 | # TODO(wj-Mcat): find out why latest pytest-asyncio will make test failed 12 | pytest-asyncio==0.18.3 13 | pytest-cov 14 | pytype 15 | semver 16 | pyee 17 | requests 18 | qrcode 19 | apscheduler 20 | lxml 21 | pre-commit 22 | mkdocs 23 | mkdocs-material 24 | types-requests 25 | mkdocstrings 26 | mkdocstrings-python-legacy 27 | yapf -------------------------------------------------------------------------------- /python-wechaty/requirements.txt: -------------------------------------------------------------------------------- 1 | pyee 2 | requests 3 | qrcode 4 | lxml 5 | wechaty-puppet>=0.4.19 6 | wechaty-puppet-service>=0.8.9 7 | quart 8 | opengraph_py3 9 | Quart-CORS 10 | APScheduler 11 | SQLAlchemy -------------------------------------------------------------------------------- /python-wechaty/scripts/check_python_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """check version""" 3 | 4 | import re 5 | import sys 6 | from typing import Tuple 7 | 8 | 9 | def version() -> Tuple[int, int, int]: 10 | """version""" 11 | try: 12 | ver = re.findall(r'^\d+\.\d+\.\d+', sys.version)[0] 13 | senior, minor, patch = re.findall(r'\d+', ver) 14 | return (int(senior), int(minor), int(patch)) 15 | 16 | # pylint: disable=W0703 17 | except Exception: 18 | return (0, 0, 0) 19 | 20 | 21 | # major, minor, patch = version() 22 | 23 | 24 | if sys.version_info < (3, 6): 25 | sys.exit('ERROR: Python 3.7 or above is required.') 26 | else: 27 | print('Python %d.%d.%d passed checking.' % sys.version_info[:3]) 28 | -------------------------------------------------------------------------------- /python-wechaty/setup.cfg: -------------------------------------------------------------------------------- 1 | # NOTE: All relative paths are relative to the location of this file. 2 | 3 | [pytype] 4 | # Space-separated list of files or directories to exclude. 5 | exclude = 6 | **/*_test.py 7 | **/test_*.py 8 | 9 | # Space-separated list of files or directories to process. 10 | inputs = 11 | . 12 | 13 | # Keep going past errors to analyze as many files as possible. 14 | keep_going = False 15 | 16 | # Run N jobs in parallel. 17 | jobs = 4 18 | 19 | # All pytype output goes here. 20 | output = .pytype 21 | 22 | # Paths to source code directories, separated by ':'. 23 | pythonpath = 24 | . 25 | 26 | # Python version (major.minor) of the target code. 27 | python_version = 3.7 28 | 29 | # Comma or space separated list of error names to ignore. 30 | disable = 31 | pyi-error 32 | # import-error 33 | 34 | # Don't report errors. 35 | report_errors = True 36 | 37 | # Experimental: Infer precise return types even for invalid function calls. 38 | precise_return = False 39 | 40 | # Experimental: solve unknown types to label with structural types. 41 | protocols = False 42 | 43 | # Experimental: Only load submodules that are explicitly imported. 44 | strict_import = False 45 | -------------------------------------------------------------------------------- /python-wechaty/setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | setup 3 | """ 4 | import os 5 | from typing import List 6 | 7 | import semver 8 | import setuptools 9 | 10 | 11 | def versioning(version: str) -> str: 12 | """ 13 | version to specification 14 | Author: Huan (https://github.com/huan) 15 | 16 | X.Y.Z -> X.Y.devZ 17 | 18 | """ 19 | sem_ver = semver.parse(version) 20 | 21 | major = sem_ver['major'] 22 | minor = sem_ver['minor'] 23 | patch = str(sem_ver['patch']) 24 | 25 | if minor % 2: 26 | patch = 'dev' + patch 27 | 28 | fin_ver = '%d.%d.%s' % ( 29 | major, 30 | minor, 31 | patch, 32 | ) 33 | 34 | return fin_ver 35 | 36 | 37 | def get_version() -> str: 38 | """ 39 | read version from VERSION file 40 | """ 41 | version = '0.0.0' 42 | 43 | with open( 44 | os.path.join( 45 | os.path.dirname(__file__), 46 | 'VERSION' 47 | ) 48 | ) as version_fh: 49 | # Get X.Y.Z 50 | version = version_fh.read().strip() 51 | # versioning from X.Y.Z to X.Y.devZ 52 | version = versioning(version) 53 | 54 | return version 55 | 56 | 57 | def get_long_description() -> str: 58 | """get long_description""" 59 | with open('README.md', 'r') as readme_fh: 60 | return readme_fh.read() 61 | 62 | 63 | def get_install_requires() -> List[str]: 64 | """get install_requires""" 65 | with open('requirements.txt', 'r') as requirements_fh: 66 | return requirements_fh.read().splitlines() 67 | 68 | 69 | setuptools.setup( 70 | name='wechaty', 71 | version=get_version(), 72 | author='Jingjing WU (吴京京)', 73 | author_email='wechaty@chatie.io', 74 | description='Wechaty is a Conversational RPA SDK for Chatbot Makers', 75 | long_description=get_long_description(), 76 | long_description_content_type='text/markdown', 77 | license='Apache-2.0', 78 | url='https://github.com/wechaty/python-wechaty', 79 | packages=setuptools.find_packages('src'), 80 | package_dir={'wechaty': 'src/wechaty'}, 81 | install_requires=get_install_requires(), 82 | classifiers=[ 83 | 'Programming Language :: Python :: 3.7', 84 | 'License :: OSI Approved :: Apache Software License', 85 | 'Operating System :: OS Independent', 86 | ], 87 | ) 88 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/__init__.py: -------------------------------------------------------------------------------- 1 | """doc""" 2 | 3 | # 4 | # import types from wechaty_puppet 5 | # 6 | 7 | from wechaty_puppet import ( 8 | FileBox, 9 | MessageType, 10 | MessagePayload, 11 | 12 | # Contact 13 | ContactGender, 14 | ContactType, 15 | ContactPayload, 16 | 17 | # Friendship 18 | FriendshipType, 19 | FriendshipPayload, 20 | 21 | # Room 22 | RoomPayload, 23 | RoomMemberPayload, 24 | 25 | # UrlLink 26 | 27 | # RoomInvitation 28 | RoomInvitationPayload, 29 | 30 | # Image 31 | ImageType, 32 | 33 | # Event 34 | EventType, 35 | EventReadyPayload, 36 | 37 | RoomQueryFilter, 38 | RoomMemberQueryFilter, 39 | FriendshipSearchQueryFilter, 40 | ContactQueryFilter, 41 | MessageQueryFilter, 42 | ScanStatus 43 | ) 44 | 45 | from .config import ( 46 | get_logger, 47 | ) 48 | from .accessory import Accessory 49 | from .plugin import ( 50 | WechatyPlugin, 51 | WechatyPluginOptions 52 | ) 53 | from .wechaty import ( 54 | Wechaty, 55 | WechatyOptions, 56 | ) 57 | from .user import ( 58 | Contact, 59 | Favorite, 60 | Friendship, 61 | Image, 62 | Message, 63 | MiniProgram, 64 | Room, 65 | RoomInvitation, 66 | Tag, 67 | UrlLink, 68 | ) 69 | from .exceptions import ( 70 | WechatyError, 71 | WechatyConfigurationError, 72 | WechatyAccessoryBindingError, 73 | WechatyStatusError, 74 | WechatyPayloadError, 75 | WechatyOperationError, 76 | WechatyPluginError, 77 | ) 78 | 79 | from .version import VERSION 80 | 81 | __version__ = VERSION 82 | 83 | __all__ = [ 84 | 'Accessory', 85 | 'Contact', 86 | 'Favorite', 87 | 'FileBox', 88 | 'Friendship', 89 | 'get_logger', 90 | 'Image', 91 | 'Message', 92 | 'MiniProgram', 93 | 'Room', 94 | 'RoomInvitation', 95 | 'Tag', 96 | 'UrlLink', 97 | 'Wechaty', 98 | 'WechatyOptions', 99 | 100 | 'WechatyPlugin', 101 | 'WechatyPluginOptions', 102 | 103 | 'MessageType', 104 | 'MessagePayload', 105 | 106 | # Contact 107 | 'ContactGender', 108 | 'ContactType', 109 | 'ContactPayload', 110 | 111 | # Friendship 112 | 'FriendshipType', 113 | 'FriendshipPayload', 114 | 115 | # Room 116 | 'RoomPayload', 117 | 'RoomMemberPayload', 118 | 119 | # UrlLink 120 | 121 | # RoomInvitation 122 | 'RoomInvitationPayload', 123 | 124 | # Image 125 | 'ImageType', 126 | 127 | # Event 128 | 'EventType', 129 | 'EventReadyPayload', 130 | 131 | 'ScanStatus', 132 | 133 | 'RoomQueryFilter', 134 | 'RoomMemberQueryFilter', 135 | 'FriendshipSearchQueryFilter', 136 | 'ContactQueryFilter', 137 | 'MessageQueryFilter', 138 | 139 | # Error 140 | 'WechatyError', 141 | 'WechatyConfigurationError', 142 | 'WechatyAccessoryBindingError', 143 | 'WechatyStatusError', 144 | 'WechatyPayloadError', 145 | 'WechatyOperationError', 146 | 'WechatyPluginError', 147 | ] 148 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/accessory.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 4 | Authors: Huan LI (李卓桓) 5 | Jingjing WU (吴京京) 6 | 7 | 2018-now @copyright Wechaty 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | """ 21 | from __future__ import annotations 22 | 23 | from typing import ( 24 | TYPE_CHECKING, 25 | # overload, 26 | # cast, 27 | Optional, 28 | TypeVar, 29 | Generic, 30 | ) 31 | 32 | from wechaty_puppet import ( 33 | get_logger, 34 | Puppet, 35 | ) 36 | 37 | from wechaty.exceptions import WechatyAccessoryBindingError 38 | 39 | # pylint:disable=R0401 40 | if TYPE_CHECKING: 41 | from .wechaty import Wechaty 42 | 43 | log = get_logger('Accessory') 44 | 45 | PayloadType = TypeVar('PayloadType') 46 | 47 | 48 | class Accessory(Generic[PayloadType]): 49 | """ 50 | Translate the function from TypeScript to Python 51 | See: https://github.com/wechaty/wechaty/blob/master/src/accessory.ts 52 | """ 53 | 54 | _puppet: Optional[Puppet] = None 55 | _wechaty: Optional[Wechaty] = None 56 | 57 | abstract: bool = True 58 | 59 | def __init__(self) -> None: 60 | if self.abstract: 61 | raise WechatyAccessoryBindingError( 62 | 'Do not instantiate class {cls} directly, sse with bot.{cls} instead. ' 63 | 'See https://github.com/wechaty/wechaty/issues/1217'.format( 64 | cls=type(self).__name__ 65 | ) 66 | ) 67 | 68 | self._payload: Optional[PayloadType] = None 69 | 70 | @property 71 | def payload(self) -> PayloadType: 72 | """ 73 | get the payload object as a property 74 | :return: 75 | """ 76 | if self._payload is None: 77 | raise ValueError( 78 | f'should ready() the {type(self).__name__} payload before get it, ' 79 | 'please call the method' 80 | ) 81 | return self._payload 82 | 83 | @payload.setter 84 | def payload(self, value: PayloadType) -> None: 85 | """ 86 | :param value: 87 | :return: 88 | """ 89 | if self._payload: 90 | log.warning('<%s> set payload more than once', self) 91 | self._payload = value 92 | 93 | def is_ready(self) -> bool: 94 | """ 95 | check if payload is ready 96 | :return: 97 | """ 98 | return self._puppet is not None and self._payload is not None 99 | 100 | @classmethod 101 | def set_puppet(cls, new_puppet: Puppet) -> None: 102 | """doc""" 103 | if cls._puppet is not None: 104 | raise AttributeError('can not set _puppet twice') 105 | cls._puppet = new_puppet 106 | 107 | @classmethod 108 | def set_wechaty(cls, new_wechaty: Wechaty) -> None: 109 | """doc""" 110 | if cls._wechaty is not None: 111 | raise AttributeError('can not set _wechaty twice') 112 | cls._wechaty = new_wechaty 113 | 114 | @classmethod 115 | def get_puppet(cls) -> Puppet: 116 | """doc""" 117 | if cls._puppet is None: 118 | raise AttributeError('puppet not found') 119 | return cls._puppet 120 | 121 | @classmethod 122 | def get_wechaty(cls) -> Wechaty: 123 | """doc""" 124 | if cls._wechaty is None: 125 | raise AttributeError('wechaty not found') 126 | return cls._wechaty 127 | 128 | @property 129 | def puppet(self) -> Puppet: 130 | """doc""" 131 | if self._puppet is None: 132 | raise AttributeError('puppet not set') 133 | return self._puppet 134 | 135 | @property 136 | def wechaty(self) -> Wechaty: 137 | """ 138 | instance property 139 | """ 140 | if self._wechaty is None: 141 | raise AttributeError('wechaty not set') 142 | return self._wechaty 143 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 4 | Authors: Huan LI (李卓桓) 5 | Jingjing WU (吴京京) 6 | 7 | 2020-now @ Copyright Wechaty 8 | 9 | Licensed under the Apache License, Version 2.0 (the 'License'); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an 'AS IS' BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | """ 21 | import os 22 | import re 23 | from typing import ( 24 | Optional, 25 | ) 26 | 27 | from wechaty_puppet import ( 28 | FileBox, 29 | get_logger 30 | ) 31 | 32 | 33 | log = get_logger('Config') 34 | 35 | # log.debug('test logging debug') 36 | # log.info('test logging info') 37 | 38 | 39 | # TODO(wj-Mcat): there is no reference usage, so need to be removed 40 | _FILE_PATH = os.path.dirname(os.path.realpath(__file__)) 41 | DATA_PATH = os.path.realpath( 42 | os.path.join( 43 | _FILE_PATH, 44 | '../data', 45 | ), 46 | ) 47 | 48 | # http://jkorpela.fi/chars/spaces.html 49 | # String.fromCharCode(8197) 50 | AT_SEPARATOR = chr(0x2005) 51 | 52 | # refer to:https://github.com/wechaty/python-wechaty/issues/285#issuecomment-997441596 53 | PARALLEL_TASK_NUM = 100 54 | 55 | 56 | def global_exception_handler(exception: Exception) -> None: 57 | """ 58 | handle the global exception 59 | :param exception: exception message 60 | :return: 61 | """ 62 | log.error('occur %s %s', exception.__class__.__name__, str(exception.args)) 63 | print(exception) 64 | 65 | 66 | class DefaultSetting(dict): 67 | """ 68 | store global default setting 69 | """ 70 | default_api_host: Optional[str] = None 71 | default_port: Optional[int] = None 72 | default_protocol: Optional[str] = None 73 | 74 | 75 | # pylint: disable=R0903 76 | def valid_api_host(api_host: str) -> bool: 77 | """ 78 | test the validation of the api_host 79 | :param api_host: 80 | :return: 81 | """ 82 | pattern = re.compile( 83 | r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|:?[0-9]*' 84 | r'([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|:?[0-9]*' 85 | r'([a-zA-Z0-9][-_.a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\.:?[0-9]*' 86 | r'([a-zA-Z]{2,13}|[a-zA-Z0-9-]{2,30}.[a-zA-Z]{2,3}):?[0-9]*$' 87 | ) 88 | return bool(pattern.match(api_host)) 89 | 90 | 91 | class Config: 92 | """ 93 | get the configuration from the environment variables 94 | """ 95 | def __init__(self) -> None: 96 | self._cache_dir: Optional[str] = None 97 | 98 | @property 99 | def cache_dir(self) -> str: 100 | """get the cache dir in the lazy loading mode 101 | 102 | Returns: 103 | str: the path of cache dir 104 | """ 105 | if self._cache_dir is not None: 106 | return self._cache_dir 107 | self._cache_dir = os.environ.get("CACHE_DIR", '.wechaty') 108 | assert self._cache_dir is not None 109 | return self._cache_dir 110 | 111 | 112 | # export const CHATIE_OFFICIAL_ACCOUNT_ID = 'gh_051c89260e5d' 113 | # chatie_official_account_id = 'gh_051c89260e5d' 114 | # CHATIE_OFFICIAL_ACCOUNT_ID = 'gh_051c89260e5d' 115 | 116 | 117 | def qr_code_for_chatie() -> FileBox: 118 | """ 119 | create QRcode for chatie 120 | :return: 121 | """ 122 | # const CHATIE_OFFICIAL_ACCOUNT_QRCODE = 123 | # 'http://weixin.qq.com/r/qymXj7DEO_1ErfTs93y5' 124 | chatie_official_account_qr_code: str = \ 125 | 'http://weixin.qq.com/r/qymXj7DEO_1ErfTs93y5' 126 | return FileBox.from_qr_code(chatie_official_account_qr_code) 127 | 128 | 129 | config = Config() 130 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 4 | Authors: Alfred Huang (黃文超) 5 | 6 | 2020-now @ Copyright Wechaty 7 | 8 | Licensed under the Apache License, Version 2.0 (the 'License'); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an 'AS IS' BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | """ 20 | 21 | 22 | from typing import Any 23 | 24 | 25 | class WechatyError(Exception): 26 | """ Wechaty error """ 27 | 28 | def __init__(self, message: str, code: Any = None, params: Any = None): 29 | super().__init__(message, code, params) 30 | 31 | self.message = message 32 | self.code = code 33 | self.params = params 34 | 35 | def __str__(self) -> str: 36 | return repr(self) 37 | 38 | 39 | class WechatyAccessoryBindingError(WechatyError): 40 | """ Raises when using Accessory classes in the wrong way """ 41 | 42 | 43 | class WechatyStatusError(WechatyError, AttributeError): 44 | """ Wechaty method calling o non-proper status (e.g. lack of await ready) """ 45 | 46 | 47 | class WechatyConfigurationError(WechatyError, AttributeError): 48 | """ Raises when configuration out of expected case """ 49 | 50 | 51 | class WechatyOperationError(WechatyError): 52 | """ Logical out of business error occurs when using wechaty """ 53 | 54 | 55 | class WechatyPluginError(WechatyError): 56 | """ Error occurs when using plugin """ 57 | 58 | 59 | class WechatyPayloadError(WechatyError, ValueError): 60 | """ Error occurs when the GRPC service return data out of expected """ 61 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/src/wechaty/py.typed -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/types.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 4 | Authors: Huan LI (李卓桓) 5 | Jingjing WU (吴京京) 6 | 7 | 2020-now @ Copyright Wechaty 8 | 9 | Licensed under the Apache License, Version 2.0 (the 'License'); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an 'AS IS' BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | """ 21 | from __future__ import annotations 22 | 23 | from typing import ( 24 | List, 25 | Optional, 26 | Union, 27 | TYPE_CHECKING, 28 | ) 29 | from abc import ABC 30 | 31 | if TYPE_CHECKING: 32 | from .user import ( 33 | Message, 34 | Contact, 35 | ) 36 | 37 | 38 | # pylint: disable=R0903 39 | class Sayable(ABC): 40 | """ 41 | wechaty sayable interface 42 | """ 43 | async def say( 44 | self, text: str, 45 | reply_to: Union[Contact, List[Contact]] 46 | ) -> Optional[Message]: 47 | """ 48 | derived classes must implement this function 49 | """ 50 | raise NotImplementedError 51 | 52 | 53 | # pylint: disable=R0903 54 | class Acceptable(ABC): 55 | """ 56 | wechaty acceptable interface 57 | """ 58 | async def accept(self) -> None: 59 | """ 60 | derived classes must implement this function 61 | """ 62 | raise NotImplementedError 63 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/__init__.py: -------------------------------------------------------------------------------- 1 | """doc""" 2 | from __future__ import annotations 3 | 4 | from .contact import Contact 5 | from .favorite import Favorite 6 | from .friendship import Friendship 7 | from .image import Image 8 | from .message import Message 9 | from .mini_program import MiniProgram 10 | from .room import Room 11 | from .tag import Tag 12 | from .url_link import UrlLink 13 | from .room_invitation import RoomInvitation 14 | from .contact_self import ContactSelf 15 | 16 | # Huan(202003): is that necessary to put "name" to `__all__`? 17 | # name = 'user' 18 | 19 | __all__ = [ 20 | 'Contact', 21 | 'Favorite', 22 | 'Friendship', 23 | 'Image', 24 | 'Message', 25 | 'MiniProgram', 26 | 'Room', 27 | 'Tag', 28 | 'UrlLink', 29 | 'RoomInvitation', 30 | 'ContactSelf' 31 | ] 32 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/contact_self.py: -------------------------------------------------------------------------------- 1 | """ContactSelf""" 2 | 3 | from __future__ import annotations 4 | from typing import Any, Optional, Type 5 | 6 | from wechaty import FileBox, get_logger 7 | from wechaty.exceptions import WechatyOperationError 8 | from wechaty.user.contact import Contact 9 | 10 | log = get_logger('ContactSelf') 11 | 12 | 13 | class ContactSelf(Contact): 14 | """ContactSelf""" 15 | 16 | async def avatar(self, file_box: Optional[FileBox] = None) -> FileBox: 17 | """ 18 | Get or set avatar of ContactSelf. 19 | 20 | Args: 21 | file_box: FileBox object, if not provided, it will return a FileBox object 22 | 23 | Examples: 24 | >>> contact_self = bot.contact_self() 25 | >>> file_box = await contact_self.avatar() 26 | >>> file_box = await contact_self.avatar(file_box) 27 | 28 | Raises: 29 | WechatyOperationError: if the contact is not self, it will not get the avatar 30 | 31 | Returns: 32 | FileBox: file_box 33 | """ 34 | log.info('avatar(%s)' % file_box.name if file_box else '') 35 | if not file_box: 36 | file_box = await super().avatar(None) 37 | return file_box 38 | 39 | if self.contact_id != self.puppet.self_id(): 40 | raise WechatyOperationError('set avatar only available for user self') 41 | 42 | await self.puppet.contact_avatar(self.contact_id, file_box) 43 | 44 | async def qr_code(self) -> str: 45 | """ 46 | Return the qrcode of ContactSelf 47 | 48 | Examples: 49 | >>> contact_self = bot.contact_self() 50 | >>> qr_code = await contact_self.qr_code() 51 | 52 | Raises: 53 | WechatyOperationError: if there is login exception, it will not get the qrcode 54 | WechatyOperationError: if the contact is not self, it will not get the qrcode 55 | 56 | Returns: 57 | str: the content of qrcode 58 | """ 59 | try: 60 | contact_id: str = self.puppet.self_id() 61 | except Exception: 62 | raise WechatyOperationError( 63 | 'Can not get qr_code, user might be either not logged in or already logged out' 64 | ) 65 | 66 | if self.contact_id != contact_id: 67 | raise WechatyOperationError('only can get qr_code for the login user self') 68 | qr_code_value = await self.puppet.contact_self_qr_code() 69 | return qr_code_value 70 | 71 | @property 72 | def name(self) -> str: 73 | """ 74 | Get the name of login contact. 75 | 76 | Examples: 77 | >>> contact_self = bot.contact_self() 78 | >>> name = contact_self.name 79 | 80 | Returns: 81 | str: the name of Login User 82 | """ 83 | return super().name 84 | 85 | async def set_name(self, name: str) -> None: 86 | """ 87 | Set the name of login contact. 88 | 89 | Args: 90 | name: new name 91 | 92 | Examples: 93 | >>> contact_self = bot.contact_self() 94 | >>> await contact_self.set_name('new name') 95 | """ 96 | await self.puppet.contact_self_name(name) 97 | await self.ready(force_sync=True) 98 | 99 | async def signature(self, signature: str) -> Any: 100 | """ 101 | Set the signature of login contact. 102 | 103 | Args: 104 | signature: new signature 105 | 106 | Examples: 107 | >>> contact_self = bot.contact_self() 108 | >>> await contact_self.signature('new signature') 109 | 110 | Raises: 111 | WechatyOperationError: if there is login exception, it will not set the signature 112 | WechatyOperationError: if the contact is not self, it will not set the signature 113 | 114 | Returns: 115 | Any: the signature of login user 116 | """ 117 | puppet_id = self.puppet.self_id() 118 | 119 | if self.contact_id != puppet_id: 120 | raise WechatyOperationError('only can get qr_code for the login user self') 121 | 122 | return self.puppet.contact_signature(signature) 123 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/favorite.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 4 | Authors: Huan LI (李卓桓) 5 | Jingjing WU (吴京京) 6 | 7 | 2020-now @ Copyright Wechaty 8 | 9 | Licensed under the Apache License, Version 2.0 (the 'License'); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an 'AS IS' BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | """ 21 | from __future__ import annotations 22 | 23 | from typing import ( 24 | TYPE_CHECKING, 25 | Any, 26 | List, 27 | ) 28 | from wechaty_puppet import get_logger 29 | 30 | if TYPE_CHECKING: 31 | from .tag import Tag 32 | 33 | log = get_logger('Favorite') 34 | 35 | 36 | # pylint: disable=R 37 | class Favorite: 38 | """ 39 | favorite object which handle the url_link content 40 | """ 41 | def __init__(self, favorite_id: str): 42 | self.favorite_id = favorite_id 43 | 44 | def get_id(self) -> str: 45 | """ 46 | get favorite_id 47 | :return: 48 | """ 49 | log.info('get_id() <%s>', self) 50 | return self.favorite_id 51 | 52 | async def tags(self) -> List[Tag]: 53 | """ 54 | get favorite tags 55 | """ 56 | # TODO -> favorite tags 57 | return [] 58 | 59 | async def find_all(self) -> Any: 60 | """ 61 | get all favorite tags 62 | """ 63 | # TODO -> find_all 64 | return 65 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 2020-now @ Copyright Wechaty 4 | 5 | GitHub: 6 | TypeScript: https://github.com/wechaty/wechaty/blob/master/src/user/image.ts 7 | Python: https://github.com/wechaty/python-wechaty/blob/master/src/wechaty/user/images.py 8 | 9 | Authors: Huan LI (李卓桓) 10 | Jingjing WU (吴京京) 11 | 12 | Licensed under the Apache License, Version 2.0 (the "License"); 13 | you may not use this file except in compliance with the License. 14 | You may obtain a copy of the License at 15 | 16 | http://www.apache.org/licenses/LICENSE-2.0 17 | 18 | Unless required by applicable law or agreed to in writing, software 19 | distributed under the License is distributed on an "AS IS" BASIS, 20 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | See the License for the specific language governing permissions and 22 | limitations under the License. 23 | """ 24 | from __future__ import annotations 25 | 26 | from typing import ( 27 | Type, 28 | ) 29 | 30 | 31 | from wechaty_puppet import ( 32 | FileBox, ImageType, get_logger 33 | ) 34 | 35 | from ..accessory import Accessory 36 | 37 | log = get_logger('Image') 38 | 39 | 40 | class Image(Accessory): 41 | """ 42 | User Image class 43 | """ 44 | 45 | def __str__(self) -> str: 46 | return 'Image<%s>' % self.image_id 47 | 48 | def __init__( 49 | self, 50 | image_id: str, 51 | ) -> None: 52 | """ 53 | :param image_id: 54 | """ 55 | super().__init__() 56 | log.info('init the message Image object <%s>', image_id) 57 | 58 | self.image_id = image_id 59 | 60 | @classmethod 61 | def create(cls: Type[Image], image_id: str) -> Image: 62 | """ 63 | create image instance by image_id 64 | :param cls: 65 | :param image_id: 66 | :return: 67 | """ 68 | log.info('@classmethod create(%s)', image_id) 69 | return cls(image_id) 70 | 71 | async def thumbnail(self) -> FileBox: 72 | """ 73 | docstring 74 | :return: 75 | """ 76 | log.info('thumbnail() for <%s>', self.image_id) 77 | image_file = await self.puppet.message_image( 78 | message_id=self.image_id, image_type=ImageType.IMAGE_TYPE_HD) 79 | return image_file 80 | 81 | async def hd(self) -> FileBox: 82 | """ 83 | docstring 84 | :return: 85 | """ 86 | log.info('hd() for <%s>', self.image_id) 87 | image_file = await self.puppet.message_image( 88 | message_id=self.image_id, image_type=ImageType.IMAGE_TYPE_HD) 89 | return image_file 90 | 91 | async def artwork(self) -> FileBox: 92 | """ 93 | docstring 94 | :return: 95 | """ 96 | log.info('artwork() for <%s>', self.image_id) 97 | image_file = await self.puppet.message_image( 98 | message_id=self.image_id, image_type=ImageType.IMAGE_TYPE_ARTWORK) 99 | return image_file 100 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/message.pyi: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from logging import Logger 3 | from ..accessory import Accessory as Accessory 4 | from .contact import Contact as Contact 5 | from .image import Image as Image 6 | from .mini_program import MiniProgram as MiniProgram 7 | from .room import Room as Room 8 | from .url_link import UrlLink as UrlLink 9 | from _typeshed import Incomplete 10 | from datetime import datetime 11 | from typing import List, Optional, Union, overload 12 | from wechaty.exceptions import WechatyOperationError as WechatyOperationError, WechatyPayloadError as WechatyPayloadError 13 | from wechaty.user.contact_self import ContactSelf as ContactSelf 14 | from wechaty.utils import timestamp_to_date as timestamp_to_date 15 | from wechaty_puppet import FileBox, MessagePayload, MessageType 16 | 17 | log: Logger 18 | SUPPORTED_MESSAGE_FILE_TYPES: List[MessageType] 19 | 20 | class Message(Accessory[MessagePayload]): 21 | Type: MessageType 22 | message_id: str 23 | 24 | def __init__(self, message_id: str) -> None: ... 25 | def message_type(self) -> MessageType: ... 26 | 27 | @overload 28 | async def say(self, msg: str) -> Optional[Message]: ... 29 | @overload 30 | async def say(self, msg: str, mention_ids: List[str] = ...) -> Optional[Message]: ... 31 | @overload 32 | async def say(self, msg: Union[Contact, FileBox, UrlLink, MiniProgram]) -> Optional[Message]: ... 33 | 34 | @classmethod 35 | async def find(cls, talker_id: Optional[str] = ..., message_id: Optional[str] = ..., room_id: Optional[str] = ..., text: Optional[str] = ..., to_id: Optional[str] = ..., message_type: Optional[MessageType] = ...) -> Optional[Message]: ... 36 | @classmethod 37 | async def find_all(cls, talker_id: Optional[str] = ..., message_id: Optional[str] = ..., room_id: Optional[str] = ..., text: Optional[str] = ..., to_id: Optional[str] = ..., message_type: Optional[MessageType] = ...) -> List[Message]: ... 38 | def talker(self) -> Contact: ... 39 | def to(self) -> Optional[Contact]: ... 40 | def room(self) -> Optional[Room]: ... 41 | def chatter(self) -> Union[Room, Contact]: ... 42 | def text(self) -> str: ... 43 | async def to_recalled(self) -> Message: ... 44 | async def recall(self) -> bool: ... 45 | @classmethod 46 | def load(cls, message_id: str) -> Message: ... 47 | def type(self) -> MessageType: ... 48 | def is_self(self) -> bool: ... 49 | async def mention_list(self) -> List[Contact]: ... 50 | async def mention_text(self) -> str: ... 51 | async def mention_self(self) -> bool: ... 52 | payload: Incomplete 53 | async def ready(self) -> None: ... 54 | async def forward(self, to: Union[Room, Contact]) -> None: ... 55 | def date(self) -> datetime: ... 56 | def age(self) -> int: ... 57 | async def to_file_box(self) -> FileBox: ... 58 | def to_image(self) -> Image: ... 59 | async def to_contact(self) -> Contact: ... 60 | async def to_url_link(self) -> UrlLink: ... 61 | async def to_mini_program(self) -> MiniProgram: ... 62 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/mini_program.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 4 | Authors: Huan LI (李卓桓) 5 | Jingjing WU (吴京京) 6 | 7 | 2020-now @ Copyright Wechaty 8 | 9 | Licensed under the Apache License, Version 2.0 (the 'License'); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an 'AS IS' BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | """ 21 | from __future__ import annotations 22 | 23 | from typing import TYPE_CHECKING 24 | from dataclasses import asdict 25 | 26 | from wechaty import Accessory 27 | from wechaty_puppet import MiniProgramPayload, get_logger 28 | from wechaty.utils import default_str 29 | 30 | if TYPE_CHECKING: 31 | from wechaty.user import Message 32 | 33 | 34 | log = get_logger('MiniProgram') 35 | 36 | 37 | class MiniProgram(Accessory[MiniProgramPayload]): 38 | """ 39 | mini_program object which handle the url_link content 40 | """ 41 | def __init__(self, payload: MiniProgramPayload): 42 | """ 43 | initialization for mini_program 44 | :param payload: 45 | """ 46 | super().__init__() 47 | 48 | log.info('MiniProgram created') 49 | self._payload: MiniProgramPayload = payload 50 | 51 | @classmethod 52 | async def create_from_message(cls, message: Message) -> MiniProgram: 53 | """ 54 | static create MiniProgram method 55 | :return: 56 | """ 57 | log.info(f'loading the mini-program from message <{message}>') 58 | 59 | mini_program_payload = await cls.get_puppet().message_mini_program( 60 | message_id=message.message_id) 61 | 62 | mini_program = MiniProgram(mini_program_payload) 63 | return mini_program 64 | 65 | @classmethod 66 | def create_from_json(cls, payload_data: dict) -> MiniProgram: 67 | """ 68 | create the mini_program from json data 69 | """ 70 | log.info(f'loading the mini-program from json data <{payload_data}>') 71 | 72 | payload = MiniProgramPayload(**payload_data) 73 | 74 | mini_program = cls(payload=payload) 75 | return mini_program 76 | 77 | def to_json(self) -> dict: 78 | """ 79 | save the mini-program to dict data 80 | """ 81 | log.info(f'save the mini-program to json data : <{self.payload}>') 82 | mini_program_data = asdict(self.payload) 83 | return mini_program_data 84 | 85 | @property 86 | def app_id(self) -> str: 87 | """ 88 | get mini_program app_id 89 | :return: 90 | """ 91 | return default_str(self._payload.appid) 92 | 93 | @property 94 | def title(self) -> str: 95 | """ 96 | get mini_program title 97 | :return: 98 | """ 99 | return default_str(self._payload.title) 100 | 101 | @property 102 | def icon_url(self) -> str: 103 | """ 104 | get mini_program icon url 105 | """ 106 | return default_str(self._payload.iconUrl) 107 | 108 | @property 109 | def page_path(self) -> str: 110 | """ 111 | get mini_program page_path 112 | :return: 113 | """ 114 | return default_str(self._payload.pagePath) 115 | 116 | @property 117 | def user_name(self) -> str: 118 | """ 119 | get mini_program user_name 120 | :return: 121 | """ 122 | return default_str(self._payload.username) 123 | 124 | @property 125 | def description(self) -> str: 126 | """ 127 | get mini_program description 128 | :return: 129 | """ 130 | return default_str(self._payload.description) 131 | 132 | @property 133 | def thumb_url(self) -> str: 134 | """ 135 | get mini_program thumb_url 136 | :return: 137 | """ 138 | return default_str(self._payload.thumbUrl) 139 | 140 | @property 141 | def thumb_key(self) -> str: 142 | """ 143 | get mini_program thumb_key 144 | :return: 145 | """ 146 | return default_str(self._payload.thumbKey) 147 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/room.pyi: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from ..accessory import Accessory as Accessory 3 | from ..config import AT_SEPARATOR as AT_SEPARATOR, PARALLEL_TASK_NUM as PARALLEL_TASK_NUM 4 | from .contact import Contact as Contact 5 | from .message import Message as Message 6 | from .mini_program import MiniProgram as MiniProgram 7 | from .url_link import UrlLink as UrlLink 8 | from _typeshed import Incomplete 9 | from typing import Any, Callable, List, Optional, Union, overload 10 | from wechaty.exceptions import WechatyOperationError as WechatyOperationError, WechatyPayloadError as WechatyPayloadError 11 | from wechaty.user.contact_self import ContactSelf as ContactSelf 12 | from wechaty.utils.async_helper import gather_with_concurrency as gather_with_concurrency 13 | from wechaty_puppet import FileBox, RoomMemberQueryFilter, RoomPayload, RoomQueryFilter 14 | 15 | log: Incomplete 16 | 17 | class Room(Accessory[RoomPayload]): 18 | room_id: Incomplete 19 | def __init__(self, room_id: str) -> None: ... 20 | def on(self, event_name: str, func: Callable[..., Any]) -> None: ... 21 | def emit(self, event_name: str, *args: Any, **kwargs: Any) -> None: ... 22 | @classmethod 23 | async def create(cls, contacts: List[Contact], topic: str) -> Room: ... 24 | @classmethod 25 | async def find_all(cls, query: Optional[Union[str, RoomQueryFilter, Callable[[Contact], bool]]] = ...) -> List[Room]: ... 26 | @classmethod 27 | async def find(cls, query: Union[str, RoomQueryFilter, Callable[[Room], bool]] = ...) -> Optional[Room]: ... 28 | @classmethod 29 | def load(cls, room_id: str) -> Room: ... 30 | payload: Incomplete 31 | async def ready(self, force_sync: bool = ..., load_members: bool = ...) -> None: ... 32 | @overload 33 | async def say(self, msg: str) -> Optional[Message]: ... 34 | @overload 35 | async def say(self, msg: str, mention_ids: List[str] = ...) -> Optional[Message]: ... 36 | @overload 37 | async def say(self, msg: Union[Contact, FileBox, UrlLink, MiniProgram]) -> Optional[Message]: ... 38 | 39 | async def add(self, contact: Contact) -> None: ... 40 | async def delete(self, contact: Contact) -> None: ... 41 | async def quit(self) -> None: ... 42 | async def topic(self, new_topic: str = ...) -> Optional[str]: ... 43 | async def announce(self, announce_text: str = ...) -> Optional[str]: ... 44 | async def qr_code(self) -> str: ... 45 | async def alias(self, member: Contact) -> Optional[str]: ... 46 | async def has(self, contact: Contact) -> bool: ... 47 | async def member_list(self, query: Union[str, RoomMemberQueryFilter] = ...) -> List[Contact]: ... 48 | async def member(self, query: Union[str, RoomMemberQueryFilter] = ...) -> Optional[Contact]: ... 49 | async def owner(self) -> Optional[Contact]: ... 50 | async def avatar(self) -> FileBox: ... 51 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/tag.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tag for Contact Message 3 | """ 4 | from __future__ import annotations 5 | 6 | from typing import ( 7 | Dict, 8 | # Optional, 9 | Union, 10 | TYPE_CHECKING 11 | ) 12 | 13 | from collections import defaultdict 14 | 15 | from wechaty.exceptions import WechatyOperationError 16 | from wechaty_puppet import get_logger 17 | from ..accessory import ( 18 | Accessory, 19 | ) 20 | 21 | if TYPE_CHECKING: 22 | from .contact import Contact 23 | from .favorite import Favorite 24 | 25 | log = get_logger('Tag') 26 | 27 | 28 | class Tag(Accessory): 29 | """ 30 | tag object which handle the url_link content 31 | """ 32 | _pool: Dict[str, 'Tag'] = defaultdict() 33 | 34 | tag_id: str 35 | 36 | def __init__(self, tag_id: str) -> None: 37 | """ 38 | initialization for tag base class 39 | :param tag_id: 40 | """ 41 | super().__init__() 42 | log.info('create tag %s', tag_id) 43 | 44 | self.tag_id = tag_id 45 | 46 | @classmethod 47 | def load(cls, tag_id: str) -> Tag: 48 | """ 49 | load tag instance 50 | """ 51 | if tag_id in cls._pool: 52 | return cls._pool[tag_id] 53 | 54 | new_tag = cls(tag_id) 55 | cls._pool[tag_id] = new_tag 56 | return new_tag 57 | 58 | @classmethod 59 | def get(cls, tag_id: str) -> Tag: 60 | """ 61 | get tag objecr 62 | """ 63 | log.info('load tag object %s', tag_id) 64 | return cls.load(tag_id) 65 | 66 | async def delete(self, target: Union[Contact, Favorite]) -> None: 67 | """ 68 | remove tag from contact or favorite 69 | :param target: 70 | :return: 71 | """ 72 | log.info('delete tag %s', self.tag_id) 73 | 74 | if target is Contact: 75 | await self.puppet.tag_contact_delete(tag_id=self.tag_id) 76 | elif target is Favorite: 77 | # TODO -> tag_favorite_delete not implement 78 | pass 79 | # await self.puppet.tag_contact_delete() 80 | else: 81 | raise WechatyOperationError('target param is required to be Contact or Favorite object') 82 | 83 | async def add(self, to: Union[Contact, Favorite]) -> None: 84 | """ 85 | add tag to contact or favorite 86 | :param to: 87 | :return: 88 | """ 89 | log.info('add tag to %s', str(to)) 90 | if isinstance(to, Contact): 91 | await self.puppet.tag_contact_add( 92 | tag_id=self.tag_id, contact_id=to.contact_id 93 | ) 94 | elif isinstance(to, Favorite): 95 | # TODO -> tag_favorite_add not implement 96 | pass 97 | # self.puppet.tag_favorite_add(self.tag_id, to) 98 | 99 | def remove(self, source: Union[Contact, Favorite]) -> None: 100 | """ 101 | Remove this tag from Contact/Favorite 102 | 103 | tips : This function is depending on the Puppet Implementation, 104 | see [puppet-compatible-table](https://github.com/wechaty/ 105 | wechaty/wiki/Puppet#3-puppet-compatible-table) 106 | :param source: 107 | :return: 108 | """ 109 | log.info('remove tag for %s with %s', 110 | self.tag_id, 111 | str(source)) 112 | try: 113 | if isinstance(source, Contact): 114 | self.puppet.tag_contact_remove( 115 | tag_id=self.tag_id, contact_id=source.contact_id) 116 | elif isinstance(source, Favorite): 117 | # TODO -> tag_favorite_remove not implement 118 | pass 119 | except Exception as e: 120 | log.info('remove exception %s', str(e.args)) 121 | raise WechatyOperationError('remove error') 122 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/user/url_link.py: -------------------------------------------------------------------------------- 1 | """ 2 | UrlLink for Contact Message 3 | """ 4 | from __future__ import annotations 5 | 6 | from typing import ( 7 | Optional, 8 | Type 9 | ) 10 | from wechaty_puppet import UrlLinkPayload, get_logger 11 | 12 | from wechaty.utils.link import get_url_metadata 13 | 14 | 15 | 16 | log = get_logger('UrlLink') 17 | 18 | 19 | class UrlLink: 20 | """ 21 | url_link object which handle the url_link content 22 | """ 23 | 24 | def __init__( 25 | self, 26 | payload: UrlLinkPayload, 27 | ): 28 | """ 29 | initialization 30 | :param payload: 31 | """ 32 | self.payload: UrlLinkPayload = payload 33 | 34 | @classmethod 35 | def create( 36 | cls: Type[UrlLink], 37 | url: str, 38 | title: Optional[str] = None, 39 | thumbnail_url: Optional[str] = None, 40 | description: Optional[str] = None 41 | ) -> UrlLink: 42 | """ 43 | create urllink from url string 44 | """ 45 | log.info('create url_link for %s', url) 46 | 47 | metadata = get_url_metadata(url) 48 | 49 | payload = UrlLinkPayload(url=url) 50 | 51 | payload.title = title or metadata.get('title', None) 52 | payload.thumbnailUrl = thumbnail_url or metadata.get('image', None) 53 | payload.description = description or metadata.get('description', None) 54 | return UrlLink(payload) 55 | 56 | def __str__(self) -> str: 57 | """ 58 | UrlLink string format output 59 | :return: 60 | """ 61 | return 'UrlLink<%s>' % self.payload.url 62 | 63 | @property 64 | def title(self) -> str: 65 | """ 66 | get UrlLink title 67 | :return: 68 | """ 69 | return self.payload.title or '' 70 | 71 | @property 72 | def thumbnailUrl(self) -> str: 73 | """ 74 | get thumbnail url 75 | :return: 76 | """ 77 | return self.payload.thumbnailUrl or '' 78 | 79 | @property 80 | def description(self) -> str: 81 | """ 82 | get description 83 | :return: 84 | """ 85 | return self.payload.description or '' 86 | 87 | @property 88 | def url(self) -> str: 89 | """ 90 | get url 91 | :return: 92 | """ 93 | return self.payload.url or '' 94 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """doc""" 2 | from .qr_code import qr_terminal 3 | from .type_check import default_str 4 | from .date_util import timestamp_to_date 5 | # from .type_check import type_check 6 | 7 | __all__ = [ 8 | 'qr_terminal', 9 | 'default_str', 10 | 'timestamp_to_date' 11 | # 'type_check' 12 | ] 13 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/utils/async_helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | async helpers 3 | """ 4 | from __future__ import annotations 5 | import asyncio 6 | from asyncio import Task 7 | from typing import ( 8 | List, 9 | Any, 10 | Optional, 11 | Set, 12 | TYPE_CHECKING 13 | ) 14 | import functools 15 | 16 | if TYPE_CHECKING: 17 | from wechaty import Message, WechatyPlugin 18 | 19 | 20 | async def gather_with_concurrency(n_task: int, tasks: List[Task]) -> Any: 21 | """ 22 | gather tasks with the specific number concurrency 23 | Args: 24 | n_task: the number of tasks 25 | tasks: task objects 26 | """ 27 | semaphore = asyncio.Semaphore(n_task) 28 | 29 | async def sem_task(task: Task) -> Any: 30 | async with semaphore: 31 | return await task 32 | return await asyncio.gather(*(sem_task(task) for task in tasks)) 33 | 34 | 35 | class SingleIdContainer: 36 | """Store the Message Id Container""" 37 | _instance: Optional[SingleIdContainer] = None 38 | 39 | def __init__(self) -> None: 40 | self.ids: Set[str] = set() 41 | self.max_size: int = 100000 42 | 43 | def exist(self, message_id: str) -> bool: 44 | """exist if the message has been emitted 45 | 46 | Args: 47 | message_id (str): the identifier of message 48 | 49 | Returns: 50 | bool: if the message is the first message 51 | """ 52 | if message_id in self.ids: 53 | return True 54 | self.ids.add(message_id) 55 | return False 56 | 57 | @classmethod 58 | def instance(cls) -> SingleIdContainer: 59 | """singleton pattern for MessageIdContainer""" 60 | if cls._instance is None or len(cls._instance.ids) > cls._instance.max_size: 61 | cls._instance = SingleIdContainer() 62 | 63 | return cls._instance 64 | 65 | 66 | def single_message(on_message_func): # type: ignore 67 | """single message decorator""" 68 | @functools.wraps(on_message_func) 69 | async def wrapper(plugin: WechatyPlugin, message: Message) -> None: 70 | if not SingleIdContainer.instance().exist(message.message_id): 71 | await on_message_func(plugin, message) 72 | return wrapper 73 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/utils/date_util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | 4 | Authors: Huan LI (李卓桓) 5 | Jingjing WU (吴京京) 6 | 7 | 2020-now @ Copyright Wechaty 8 | 9 | Licensed under the Apache License, Version 2.0 (the 'License'); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an 'AS IS' BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | """ 21 | from __future__ import annotations 22 | from datetime import datetime 23 | 24 | 25 | def timestamp_to_date(timestamp: float) -> datetime: 26 | """convert different timestamp precision to python format 27 | 28 | Python2.7: https://docs.python.org/2.7/library/datetime.html#datetime.datetime 29 | Python3+ :https://docs.python.org/3.7/library/datetime.html#datetime.datetime 30 | for datetime.fromtimestamp. It’s common for this to be restricted to years from 1970 through 2038. 31 | 2145888000 is 2038-01-01 00:00:00 UTC for second 32 | 2145888000 is 1970-01-26 04:04:48 UTC for millisecond 33 | 34 | Args: 35 | timestamp (float): from different source, so, has different 36 | timestamp precision 37 | """ 38 | if timestamp > 2145888000: 39 | timestamp = timestamp / 1000 40 | return datetime.fromtimestamp(timestamp) 41 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/utils/link.py: -------------------------------------------------------------------------------- 1 | """link helper function for fetching the friendly meta data from url link""" 2 | from __future__ import annotations 3 | from typing import Any, Dict 4 | import requests 5 | from opengraph_py3 import OpenGraph # type: ignore 6 | 7 | 8 | def get_url_metadata(url: str) -> Dict[str, Any]: 9 | """get open graph meta data open open graph protocol 10 | 11 | The Open Graph Protocol: https://ogp.me/ 12 | 13 | Args: 14 | url (str): the url of link 15 | 16 | Returns: 17 | Dict[str, Any]: the meta data 18 | """ 19 | return OpenGraph(url=url) 20 | 21 | 22 | def fetch_github_user_avatar_url(name: str) -> str: 23 | """fetch_github_user_avatar_url 24 | 25 | refer to: https://docs.github.com/en/rest/users/users#get-a-user 26 | 27 | Args: 28 | name (str): the name of github user 29 | 30 | Returns: 31 | str: the avatar url of github user 32 | """ 33 | source_avatar_url = f'https://api.github.com/users/{name}' 34 | 35 | header = { 36 | "accept": "application/vnd.github.v3+jso" 37 | } 38 | response = requests.get(source_avatar_url, headers=header) 39 | data = response.json() 40 | return data.get('avatar_url', None) -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/utils/qr_code.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | qr_code helper utils 4 | """ 5 | from typing import Any 6 | import qrcode 7 | 8 | 9 | def qr_terminal(data: str, version: Any = None) -> None: 10 | """print the qrcode to the terminal using the python-qrcode tools 11 | 12 | https://github.com/lincolnloop/python-qrcode 13 | 14 | Args: 15 | data (str): the data of the qrcode 16 | version (Any, optional): the qrcode version. Defaults to None. 17 | """ 18 | qr = qrcode.QRCode(version, border=2) 19 | qr.add_data(data) 20 | if version: 21 | qr.make() 22 | else: 23 | qr.make(fit=True) 24 | qr.print_ascii(invert=True) 25 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/utils/qrcode_terminal.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | import qrcode 3 | import platform 4 | 5 | 6 | def qr_terminal_str(data: Any, version: Any = None) -> str: 7 | """ 8 | 9 | :param data: qrcode data 10 | :param version:1-40 or None 11 | :return: 12 | """ 13 | if platform.system() == "Windows": 14 | white_block = '▇' 15 | black_block = ' ' 16 | new_line = '\n' 17 | else: 18 | white_block = '\033[0;37;47m ' 19 | black_block = '\033[0;37;40m ' 20 | new_line = '\033[0m\n' 21 | 22 | qr = qrcode.QRCode(version) 23 | qr.add_data(data) 24 | if version: 25 | qr.make() 26 | else: 27 | qr.make(fit=True) 28 | output = white_block * (qr.modules_count + 2) + new_line 29 | for mn in qr.modules: 30 | output += white_block 31 | for m in mn: 32 | if m: 33 | output += black_block 34 | else: 35 | output += white_block 36 | output += white_block + new_line 37 | output += white_block * (qr.modules_count + 2) + new_line 38 | return output 39 | 40 | 41 | def draw(data: Any, version: Any = None) -> None: 42 | """doc""" 43 | output = qr_terminal_str(data, version) 44 | print(output) 45 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/utils/type_check.py: -------------------------------------------------------------------------------- 1 | """add type check utils""" 2 | from typing import Union, Optional 3 | 4 | 5 | def type_check(obj: object, type_name: str) -> bool: 6 | """ 7 | circulation dependency problems can be resolved by TYPE_CHECKING, 8 | but this can not resolve NO type linting problems. eg: 9 | if isinstance(msg, Contact): 10 | pass 11 | in this problem, program don't import Contact at running time. So, it will 12 | throw a Exception, which will not be threw 13 | :param obj: 14 | :param type_name: 15 | :return: 16 | """ 17 | if hasattr(obj, '__class__') and hasattr(obj.__class__, '__name__'): 18 | return obj.__class__.__name__ == type_name 19 | return False 20 | 21 | 22 | def default_str(obj: Union[str, Optional[str]]) -> str: 23 | if obj: 24 | return obj 25 | return '' 26 | -------------------------------------------------------------------------------- /python-wechaty/src/wechaty/version.py: -------------------------------------------------------------------------------- 1 | """ 2 | Do not edit this file. 3 | This file will be auto-generated before deploy. 4 | """ 5 | VERSION = '0.0.0' 6 | -------------------------------------------------------------------------------- /python-wechaty/tests/config_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | config unit test 3 | """ 4 | from typing import ( 5 | Any, 6 | # Dict, 7 | Iterable, 8 | ) 9 | 10 | import pytest 11 | 12 | from wechaty_puppet import get_logger 13 | 14 | # pylint: disable=C0103 15 | log = get_logger('ConfigTest') 16 | 17 | # pylint: disable=redefined-outer-name 18 | 19 | 20 | # https://stackoverflow.com/a/57015304/1123955 21 | @pytest.fixture(name='data', scope='module') 22 | def fixture_data() -> Iterable[str]: 23 | """ doc """ 24 | yield 'test' 25 | 26 | 27 | def test_config( 28 | data: Any, 29 | ) -> None: 30 | """ 31 | Unit Test for config function 32 | """ 33 | print(data) 34 | 35 | assert data == 'test', 'data should equals test' 36 | 37 | 38 | def test_get_logger() -> None: 39 | """test""" 40 | assert get_logger, 'log should exist' 41 | -------------------------------------------------------------------------------- /python-wechaty/tests/room_test.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | import pytest 3 | from wechaty.wechaty import Wechaty # noqa 4 | 5 | 6 | @pytest.mark.asyncio 7 | async def test_room_owner(test_bot: Wechaty) -> None: 8 | owner = await test_bot.Room("fake_room").owner() 9 | await owner.ready() 10 | assert owner.contact_id == "wechaty_user" 11 | 12 | 13 | @pytest.mark.asyncio 14 | async def test_room_topic(test_bot: Wechaty) -> None: 15 | topic = await test_bot.Room("fake_room").topic() 16 | assert topic == "fake_room" 17 | 18 | 19 | @pytest.mark.parametrize( 20 | ("room_name", "res"), [("test", "test_room"), ("fake", "fake_room"), ("wechaty", None)] 21 | ) 22 | @pytest.mark.asyncio 23 | async def test_room_find(test_bot: Wechaty, room_name: str, res: Union[str, None]) -> None: 24 | room = await test_bot.Room.find(room_name) 25 | name = room.room_id if room else None 26 | assert name == res 27 | 28 | 29 | @pytest.mark.parametrize( 30 | ("room_name", "res"), [("test", 1), ("fake", 1), ("room", 2), ("wechaty", 0)] 31 | ) 32 | @pytest.mark.asyncio 33 | async def test_room_findall(test_bot: Wechaty, room_name: str, res: int) -> None: 34 | room = await test_bot.Room.find_all(room_name) 35 | assert len(room) == res 36 | -------------------------------------------------------------------------------- /python-wechaty/tests/smoke_testing_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Unit Test 3 | """ 4 | # pylint: disable=W0621 5 | 6 | # from typing import ( 7 | # # Any, 8 | # Iterable, 9 | # ) 10 | 11 | import pytest 12 | 13 | # from agent import Agent 14 | 15 | 16 | def test_smoke_testing() -> None: 17 | """ wechaty """ 18 | assert pytest, 'should True' 19 | -------------------------------------------------------------------------------- /python-wechaty/tests/timestamp_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Unit test 3 | """ 4 | from wechaty.utils import timestamp_to_date 5 | 6 | 7 | def test_timestamp_with_millisecond_precision() -> None: 8 | timestamp = timestamp_to_date(1600849574736) 9 | assert timestamp is not None 10 | 11 | 12 | def test_timestamp_with_microsecond_precision() -> None: 13 | timestamp = timestamp_to_date(1600849792.367416) 14 | assert timestamp is not None 15 | -------------------------------------------------------------------------------- /python-wechaty/tests/url_link_test.py: -------------------------------------------------------------------------------- 1 | """unit test for urllink""" 2 | from __future__ import annotations 3 | 4 | from wechaty.user.url_link import UrlLink 5 | 6 | 7 | def test_create(): 8 | """unit test for creating""" 9 | UrlLink.create( 10 | url='https://github.com/wechaty/python-wechaty/issues/339', 11 | title='title', 12 | thumbnail_url='thu', 13 | description='simple desc' 14 | ) 15 | -------------------------------------------------------------------------------- /python-wechaty/tests/user_message_test.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from wechaty.wechaty import Wechaty 3 | 4 | 5 | @pytest.mark.asyncio 6 | async def test_mention_text_without_mentions(test_bot: Wechaty) -> None: 7 | """Test extracting mention text from a message without mentions""" 8 | msg = await test_bot.Message.find(message_id="no_mention") 9 | await msg.ready() 10 | text = await msg.mention_text() 11 | assert text == 'foo bar asd' 12 | 13 | 14 | @pytest.mark.asyncio 15 | async def test_mention_text_without_mentions_in_room(test_bot: Wechaty) -> None: 16 | """Test extracting mention text from a message without mentions""" 17 | msg = await test_bot.Message.find(message_id="room_no_mention") 18 | await msg.ready() 19 | text = await msg.mention_text() 20 | assert text == 'beep' 21 | 22 | 23 | @pytest.mark.asyncio 24 | async def test_mention_text_with_mentions_in_room(test_bot: Wechaty) -> None: 25 | """Test extracting mention text from a message without mentions""" 26 | msg = await test_bot.Message.find(message_id="room_with_mentions") 27 | await msg.ready() 28 | text = await msg.mention_text() 29 | assert text == 'test message asd' 30 | 31 | 32 | @pytest.mark.asyncio 33 | async def test_mention_text_with_mentions_and_alias_in_room(test_bot: Wechaty) -> None: 34 | """Test extracting mention text from a message without mentions""" 35 | msg = await test_bot.Message.find(message_id="room_with_mentions_and_alias") 36 | await msg.ready() 37 | text = await msg.mention_text() 38 | assert text == '123123 kkasd' 39 | 40 | 41 | @pytest.mark.asyncio 42 | async def test_mention_text_with_mentions_and_mismatched_alias(test_bot: Wechaty) -> None: 43 | """Test extracting mention text from a message without mentions""" 44 | msg = await test_bot.Message.find(message_id="room_with_mentions_and_alias_mismatched") 45 | await msg.ready() 46 | text = await msg.mention_text() 47 | assert text == '123123@Fake User beep' 48 | 49 | 50 | @pytest.mark.asyncio 51 | async def test_mention_text_with_mentions_but_not_mention_data(test_bot: Wechaty) -> None: 52 | """Test extracting mention text from a message without mentions""" 53 | msg = await test_bot.Message.find(message_id="room_with_text_mentions") 54 | await msg.ready() 55 | text = await msg.mention_text() 56 | assert text == '@Wechaty User @Test User @Fake Alias beep!!' 57 | -------------------------------------------------------------------------------- /python-wechaty/tests/utils_test.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import pytest 3 | from wechaty.utils.async_helper import gather_with_concurrency 4 | from wechaty.utils.link import fetch_github_user_avatar_url, get_url_metadata 5 | from wechaty.utils.async_helper import SingleIdContainer 6 | 7 | 8 | async def number_task(num: int): 9 | """ 10 | just return the original number 11 | """ 12 | return num 13 | 14 | 15 | @pytest.mark.asyncio 16 | async def test_gather_tasks_with_n_concurrency(): 17 | tasks = [asyncio.create_task(number_task(i)) for i in range(1000)] 18 | sum_value = (0 + 999) * 1000 / 2 19 | result = await gather_with_concurrency(10, tasks) 20 | assert sum_value == sum(result), 'the final sum value is not correct' 21 | 22 | 23 | def test_fetch_metadata(): 24 | metadata = get_url_metadata('http://github.com/') 25 | assert 'title' in metadata 26 | assert 'image' in metadata 27 | 28 | 29 | def test_fetch_github_user_avatar(): 30 | avatar = fetch_github_user_avatar_url('wj-Mcat') 31 | assert avatar is not None 32 | assert 'avatars.githubusercontent.com' in avatar 33 | 34 | 35 | def test_single_id_container(): 36 | assert not SingleIdContainer.instance().exist('-1') 37 | assert SingleIdContainer.instance().exist('-1') 38 | 39 | for index in range(SingleIdContainer.instance().max_size): 40 | assert not SingleIdContainer.instance().exist(index) 41 | 42 | assert len(SingleIdContainer.instance().ids) == 0 -------------------------------------------------------------------------------- /python-wechaty/tests/version_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | version unit test 3 | """ 4 | # import pytest # type: ignore 5 | 6 | from wechaty.version import VERSION 7 | 8 | 9 | def test_version() -> None: 10 | """ 11 | Unit Test for version file 12 | """ 13 | 14 | assert VERSION == '0.0.0', 'version should be 0.0.0' 15 | -------------------------------------------------------------------------------- /python-wechaty/tests/wechaty_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Unit test 3 | """ 4 | import pytest 5 | from wechaty_puppet import WechatyPuppetConfigurationError 6 | from wechaty import Wechaty, WechatyOptions 7 | 8 | def test_constructor(): 9 | with pytest.raises(WechatyPuppetConfigurationError): 10 | bot = Wechaty() 11 | 12 | options = WechatyOptions(token='fake-token', endpoint='127.0.0.1:8080') 13 | bot = Wechaty(options=options) 14 | 15 | assert bot.puppet.options.token == 'fake-token' 16 | assert bot.puppet.options.end_point == '127.0.0.1:8080' 17 | -------------------------------------------------------------------------------- /python-wechaty/wip/wechaty/__init__.py: -------------------------------------------------------------------------------- 1 | """doc""" 2 | from __future__ import annotations 3 | 4 | from typing import ( 5 | Optional, 6 | ) 7 | 8 | 9 | class Wechaty: 10 | """Working In Progress""" 11 | 12 | def __init__(self) -> None: 13 | """WIP Warning""" 14 | print(''' 15 | 16 | Dear Python Wechaty user, 17 | 18 | Thank you very much for using Python Wechaty! 19 | 20 | Wechaty is a RPA SDK for Wechat Individual Account that can help you create a chatbot in 6 lines of Python. 21 | Our GitHub is at https://github.com/wechaty/python-wechaty 22 | 23 | Today, we are under the process of translating the TypeScript Wechaty to Python Wechaty, please see issue #11 "From TypeScript to Python in Wechaty Way - Internal Modules" at https://github.com/wechaty/python-wechaty/issues/11 24 | 25 | To stay tuned, watch our repository now! 26 | 27 | Please also feel free to leave comments in our issues if you want to contribute, testers, coders, and doc writers are all welcome! 28 | 29 | Huan 30 | Author of Wechaty 31 | Mar 15, 2020 32 | 33 | ''') 34 | 35 | def version(self) -> str: 36 | """version""" 37 | type(self) 38 | return '0.0.0' 39 | 40 | def ding( 41 | self: Wechaty, 42 | data: Optional[str], 43 | ) -> None: 44 | """ding""" 45 | type(self) 46 | type(data) 47 | 48 | 49 | __all__ = [ 50 | 'Wechaty', 51 | ] 52 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.log 3 | failed_stories.md 4 | story_confmat.md 5 | test_env 6 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/Makefile: -------------------------------------------------------------------------------- 1 | train: 2 | rasa train --domain domain.yml --data data --config config.yml --out models 3 | 4 | train-nlu: 5 | rasa train nlu --nlu split_data/nlu --config configs.yml --out models/nlu 6 | 7 | run-actions: 8 | rasa run actions 9 | 10 | shell: 11 | make run-actions & 12 | rasa shell -m models --endpoints configs/endpoints.yml 13 | 14 | run: 15 | make run-actions & 16 | rasa run --enable-api -m models --endpoints configs/endpoints.yml -p 5005 17 | 18 | run-x: 19 | make run-actions & 20 | rasa x --no-prompt -c configs.yml --cors "*" --endpoints configs/endpoints.yml --enable-api -------------------------------------------------------------------------------- /rasa_chatbot_cn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/rasa_chatbot_cn/__init__.py -------------------------------------------------------------------------------- /rasa_chatbot_cn/actions/actions.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | 6 | from rasa_core_sdk import Action 7 | from rasa_core_sdk.events import SlotSet 8 | 9 | 10 | support_search = ["话费", "流量"] 11 | 12 | 13 | def extract_item(item): 14 | if item is None: 15 | return None 16 | for name in support_search: 17 | if name in item: 18 | return name 19 | return None 20 | 21 | 22 | class ActionSearchConsume(Action): 23 | def name(self): 24 | return 'action_search_consume' 25 | 26 | def run(self, dispatcher, tracker, domain): 27 | item = tracker.get_slot("item") 28 | item = extract_item(item) 29 | if item is None: 30 | dispatcher.utter_message("您好,我现在只会查话费和流量") 31 | dispatcher.utter_message("你可以这样问我:“帮我查话费”") 32 | return [] 33 | 34 | time = tracker.get_slot("time") 35 | if time is None: 36 | dispatcher.utter_message("您想查询哪个月的消费?") 37 | return [] 38 | # query database here using item and time as key. but you may normalize time format first. 39 | dispatcher.utter_message("好,请稍等") 40 | if item == "流量": 41 | dispatcher.utter_message( 42 | "您好,您{}共使用{}二百八十兆,剩余三十兆。".format(time, item)) 43 | else: 44 | dispatcher.utter_message("您好,您{}共消费二十八元。".format(time)) 45 | return [] 46 | 47 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/config.yml: -------------------------------------------------------------------------------- 1 | language: zh 2 | pipeline: 3 | - name: HFTransformersNLP 4 | model_weights: "bert-base-chinese" 5 | model_name: "bert" 6 | 7 | - name: LanguageModelTokenizer 8 | - name: LanguageModelFeaturizer 9 | 10 | - name: CountVectorsFeaturizer 11 | - name: CountVectorsFeaturizer 12 | analyzer: char_wb 13 | min_ngram: 1 14 | max_ngram: 4 15 | 16 | - name: DIETClassifier 17 | epochs: 30 18 | learning_rate: 0.005 19 | num_transformer_layers: 0 20 | embedding_dimension: 30 21 | weight_sparcity: 0.8 22 | hidden_layer_sizes: 23 | text: [256, 128] 24 | 25 | policies: 26 | - name: AugmentedMemoizationPolicy 27 | 28 | - name: TEDPolicy 29 | epochs: 100 30 | featurizer: 31 | - name: MaxHistoryTrackerFeaturizer 32 | max_history: 5 33 | state_featurizer: 34 | - name: BinarySingleStateFeaturizer 35 | 36 | 37 | - name: FallbackPolicy 38 | fallback_action_name: 'action_default_fallback' 39 | nlu_threshold: 0.5 40 | core_threshold: 0.3 41 | ambiguity_threshold: 0.1 42 | 43 | - name: MappingPolicy 44 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/configs/endpoints.yml: -------------------------------------------------------------------------------- 1 | action_endpoint: 2 | url: http://localhost:5055/webhook 3 | 4 | # tracker_store: 5 | # store_type: mongod 6 | # url: mongodb://mongo:27017 7 | # db: chatbot -------------------------------------------------------------------------------- /rasa_chatbot_cn/data/core/stories.md: -------------------------------------------------------------------------------- 1 | ## Generated Story No1 2 | * greet 3 | - utter_greet 4 | * deny 5 | - utter_goodbye 6 | 7 | ## Generated Story No2 8 | * greet 9 | - utter_greet 10 | * goodbye 11 | - utter_goodbye 12 | 13 | ## Generated Story No3 14 | * greet 15 | - utter_greet 16 | * thanks 17 | - utter_thanks 18 | 19 | ## Generated Story No4 20 | * unknown_intent 21 | - action_default_fallback 22 | 23 | ## Generated Story No5 24 | * greet 25 | - utter_greet 26 | * request_search{"item": "\u8bdd\u8d39"} 27 | - slot{"item": "\u8bdd\u8d39"} 28 | - utter_ask_time 29 | * inform{"time": "\u4e09\u6708"} 30 | - slot{"time": "\u4e09\u6708"} 31 | - utter_confirm 32 | * confirm 33 | - action_search_consume 34 | - utter_ask_morehelp 35 | * deny 36 | - utter_goodbye 37 | 38 | 39 | ## Generated Story No6 40 | * greet 41 | - utter_greet 42 | * request_search{"item": "\u6d41\u91cf", "time": "\u4e09\u6708"} 43 | - slot{"item": "\u6d41\u91cf"} 44 | - slot{"time": "\u4e09\u6708"} 45 | - utter_confirm 46 | * confirm 47 | - action_search_consume 48 | - utter_ask_morehelp 49 | * deny 50 | - utter_goodbye 51 | 52 | 53 | ## Generated Story No7 54 | * greet 55 | - utter_greet 56 | * request_management{"package": "\u5957\u9910"} 57 | - slot{"package": "\u5957\u9910"} 58 | - utter_ask_package 59 | * inform{"package": "\u5957\u9910\u4e00"} 60 | - slot{"package": "\u5957\u9910\u4e00"} 61 | - utter_confirm 62 | * confirm 63 | - utter_ack_management 64 | - utter_ask_morehelp 65 | * deny 66 | - utter_goodbye 67 | * thanks 68 | - utter_thanks 69 | 70 | ## Generated Story No8 71 | * greet 72 | - utter_greet 73 | * request_management{"package": "\u5957\u9910"} 74 | - slot{"package": "\u5957\u9910"} 75 | - utter_ask_package 76 | * inform{"package": "\u5957\u9910\u4e00"} 77 | - slot{"package": "\u5957\u9910\u4e00"} 78 | - utter_confirm 79 | * confirm 80 | - utter_ack_management 81 | - utter_ask_morehelp 82 | * request_search{"item": "\u6d41\u91cf", "time": "\u4e09\u6708"} 83 | - slot{"item": "\u6d41\u91cf"} 84 | - slot{"time": "\u4e09\u6708"} 85 | - utter_confirm 86 | * confirm 87 | - action_search_consume 88 | - utter_ask_morehelp 89 | * deny 90 | - utter_goodbye 91 | 92 | ## Generated Story No9 93 | * greet 94 | - utter_greet 95 | * request_management{"package": "\u5957\u9910"} 96 | - slot{"package": "\u5957\u9910"} 97 | - utter_ask_package 98 | * request_search{"item": "\u6d41\u91cf", "time": "\u4e09\u6708"} 99 | - slot{"item": "\u6d41\u91cf"} 100 | - slot{"time": "\u4e09\u6708"} 101 | - utter_confirm 102 | * confirm 103 | - action_search_consume 104 | - utter_ask_morehelp 105 | * deny 106 | - utter_goodbye 107 | 108 | ## Generated Story No10 109 | * greet 110 | - utter_greet 111 | * request_management{"package": "\u5957\u9910"} 112 | - slot{"package": "\u5957\u9910"} 113 | - utter_ask_package 114 | * request_search{"item": "\u6d41\u91cf"} 115 | - slot{"item": "\u6d41\u91cf"} 116 | - utter_ask_time 117 | * inform{"time": "\u4e09\u6708"} 118 | - slot{"time": "\u4e09\u6708"} 119 | - utter_confirm 120 | * confirm 121 | - action_search_consume 122 | - utter_ask_morehelp 123 | * deny 124 | - utter_goodbye -------------------------------------------------------------------------------- /rasa_chatbot_cn/dev/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM python:3.6-slim 2 | 3 | SHELL ["/bin/bash", "-c"] 4 | 5 | # Default to UTF-8 file.encoding 6 | ENV LANG C.UTF-8 7 | 8 | RUN apt-get update -qq && \ 9 | apt-get install -y --no-install-recommends \ 10 | apt-utils \ 11 | vim \ 12 | procps \ 13 | build-essential \ 14 | wget \ 15 | unzip \ 16 | openssh-client \ 17 | graphviz-dev \ 18 | pkg-config \ 19 | git-core \ 20 | openssl \ 21 | libssl-dev \ 22 | libffi6 \ 23 | libffi-dev \ 24 | libpng-dev \ 25 | curl \ 26 | python3-tk \ 27 | tk-dev && \ 28 | apt-get clean && \ 29 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ 30 | mkdir /app 31 | 32 | WORKDIR /app 33 | 34 | ENV PYTHONPATH "${PYTHONPATH}:/app" 35 | 36 | COPY requirements.txt ./ 37 | 38 | RUN pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/ && \ 39 | pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ 40 | 41 | ENTRYPOINT ["/bin/sh"] 42 | CMD ["/app/dev/run.sh"] 43 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/dev/deploy_dev.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [ ! "$(docker ps -a | grep mongo)" ] 3 | then echo "start mongo db docker"; 4 | mkdir ~/data; 5 | docker pull mongo; 6 | docker run --name mongo -p 27017:27017 -v ~/data:/data/db -itd mongo; 7 | fi 8 | 9 | CHATBOT_VERSION=0.0.1 10 | 11 | docker build -f dev/Dockerfile.dev . -t chatbot_dev:$CHATBOT_VERSION 12 | docker run --name chatbot_dev -p 5005:5005 -p 5002:5002 --link mongo -v "$PWD":/app -itd chatbot_dev:$CHATBOT_VERSION 13 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/dev/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | make train 4 | make run 5 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/domain.yml: -------------------------------------------------------------------------------- 1 | slots: 2 | item: 3 | type: text 4 | time: 5 | type: text 6 | phone_number: 7 | type: text 8 | price: 9 | type: text 10 | package: 11 | type: text 12 | 13 | intents: 14 | - greet 15 | - confirm 16 | - goodbye 17 | - thanks 18 | - inform 19 | - request_management 20 | - request_search 21 | - deny 22 | - unknown_intent 23 | 24 | entities: 25 | - item 26 | - time 27 | - phone_number 28 | - price 29 | - package 30 | 31 | templates: 32 | utter_greet: 33 | - text: "您好! 我是机器人小贝,很高兴为您服务。" 34 | - text: "你好! 我是小贝,可以帮您办理流量套餐,话费查询等业务。" 35 | - text: "hi! 我是小贝,有什么可以帮您吗。" 36 | utter_goodbye: 37 | - text: "再见,为您服务很开心" 38 | - text: "Bye, 下次再见" 39 | utter_default: 40 | - text: "您说什么,小贝听不懂。" 41 | - text: "你说的小贝不懂呀!" 42 | - text: "不好意思,您能换个说法吗?" 43 | utter_thanks: 44 | - text: "不用谢" 45 | - text: "我应该做的" 46 | - text: "您开心我就开心" 47 | utter_ask_morehelp: 48 | - text: "还有什么能帮您吗?" 49 | - text: "您还需要什么服务?" 50 | utter_ask_time: 51 | - text: "你想查哪个时间段的?" 52 | - text: "你想查几月份的?" 53 | utter_ask_phonenum: 54 | - text: "你想查的手机号码是?" 55 | utter_confirm: 56 | - text: "你确认吗?" 57 | utter_ask_package: 58 | - text: "我们现在支持办理流量套餐:套餐一:二十元包月三十兆;套餐二:四十元包月八十兆,请问您需要哪个?" 59 | - text: "我们有如下套餐供您选择:套餐一:二十元包月三十兆;套餐二:四十元包月八十兆,请问您需要哪个?" 60 | utter_ack_management: 61 | - text: "已经为您办理好了{package}" 62 | 63 | actions: 64 | - utter_greet 65 | - utter_goodbye 66 | - utter_default 67 | - utter_thanks 68 | - utter_ask_morehelp 69 | - utter_ask_time 70 | - utter_ask_package 71 | - utter_ack_management 72 | - utter_ask_phonenum 73 | - utter_confirm 74 | - action_search_consume 75 | -------------------------------------------------------------------------------- /rasa_chatbot_cn/process.py: -------------------------------------------------------------------------------- 1 | import urllib3 2 | import json 3 | import os 4 | import logging 5 | 6 | 7 | class RasaIntent: 8 | """ 9 | @基于rasa的通用intent意图识别 10 | @参考https://github.com/bigbrother666sh/Awada 11 | """ 12 | def __init__( 13 | self, 14 | logs: str = '.utils', 15 | port: str = '5005', 16 | ) -> None: 17 | # 1. create the cache_dir 18 | self.cache_dir = logs 19 | os.makedirs(self.cache_dir, exist_ok=True) 20 | 21 | # 2. save the log info into .log file 22 | log_formatter = logging.Formatter(fmt='%(levelname)s - %(message)s') 23 | self.logger = logging.getLogger('rasaintent') 24 | self.logger.handlers = [] 25 | self.logger.setLevel('INFO') 26 | self.logger.propagate = False 27 | log_file = os.path.join(self.cache_dir, 'intent_LTE.log') 28 | 29 | file_handler = logging.FileHandler(log_file, 'a', encoding='utf-8') 30 | file_handler.setLevel('INFO') 31 | file_handler.setFormatter(log_formatter) 32 | self.logger.addHandler(file_handler) 33 | 34 | # 3. create the http client 35 | self.http = urllib3.PoolManager() 36 | 37 | # 4. create the rasa url 38 | self.url = 'http://localhost:' + port + '/parse' 39 | 40 | # 3. create the http client 41 | self.rasa_url = 'http://localhost:'+port+'/model/parse' 42 | self.http = urllib3.PoolManager() 43 | 44 | _test_data = {'text': '我想邀请你一起投资'} 45 | _encoded_data = json.dumps(_test_data) 46 | _test_res = self.http.request('POST', self.rasa_url, body=_encoded_data) 47 | _result = json.loads(_test_res.data) 48 | 49 | if not _result: 50 | raise RuntimeError('Rasa server not running, pls start it first and trans the right port in str') 51 | 52 | def predict(self, text: str): 53 | _test_data = {'text': text} 54 | _encoded_data = json.dumps(_test_data) 55 | _test_res = self.http.request('POST', self.rasa_url, body=_encoded_data) 56 | _result = json.loads(_test_res.data) 57 | _intent = _result['intent']['name'] 58 | _conf = _result['intent']['confidence'] 59 | if _conf >= 0.5 and _intent != 'nlu_fallback': 60 | self.logger.info(f'text: {text}---Intent: {_intent} confidence: {_conf}') 61 | else: 62 | self.logger.warning(f'text: {text}---Intent: {_intent} confidence: {_conf}') 63 | return _intent, _conf 64 | 65 | 66 | if __name__ == "__main__": 67 | nlu_intent = RasaIntent() 68 | print("====意图测试====") 69 | 70 | while True: 71 | print("输入Q退出") 72 | prompt = input("输入待测文本:") 73 | if prompt.lower() == "q": 74 | break 75 | intent, conf = nlu_intent.predict(prompt) 76 | print("检测出意图:", intent) 77 | print("意图置信度:", conf) -------------------------------------------------------------------------------- /rooms.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/rooms.json -------------------------------------------------------------------------------- /script/源1.0预训练语言模型使用示例V1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/script/源1.0预训练语言模型使用示例V1.pdf -------------------------------------------------------------------------------- /script/源APIExp使用手册0510.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/script/源APIExp使用手册0510.pdf -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python Wechaty - https://github.com/wechaty/python-wechaty 3 | Authors: Huan LI (李卓桓) 4 | Jingjing WU (吴京京) 5 | 2020 @ Copyright Wechaty Contributors 6 | Licensed under the Apache License, Version 2.0 (the 'License'); 7 | you may not use this file except in compliance with the License. 8 | 9 | You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an 'AS IS' BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and limitations under the License. 14 | """ 15 | import os 16 | import asyncio 17 | import subprocess 18 | 19 | from urllib.parse import quote 20 | 21 | from wechaty import ( 22 | Contact, 23 | FileBox, 24 | Message, 25 | Wechaty, 26 | ScanStatus, 27 | ) 28 | 29 | 30 | async def on_message(msg: Message): 31 | """ 32 | Message Handler for the Bot 33 | """ 34 | if msg.text() == 'ding': 35 | await msg.say('dong') 36 | 37 | file_box = FileBox.from_url( 38 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/' 39 | 'u=1116676390,2305043183&fm=26&gp=0.jpg', 40 | name='ding-dong.jpg' 41 | ) 42 | try: 43 | await msg.say(file_box) 44 | except Exception as e: 45 | print("catch some errors:", e) 46 | 47 | 48 | async def on_scan( 49 | qrcode: str, 50 | status: ScanStatus, 51 | _data, 52 | ): 53 | """ 54 | Scan Handler for the Bot 55 | """ 56 | print('Status: ' + str(status)) 57 | print('View QR Code Online: https://wechaty.js.org/qrcode/' + quote(qrcode)) 58 | 59 | 60 | async def on_login(user: Contact): 61 | """ 62 | Login Handler for the Bot 63 | """ 64 | print(user) 65 | # TODO: To be written 66 | 67 | 68 | async def main(): 69 | """ 70 | Async Main Entry 71 | """ 72 | # 73 | # Make sure we have set WECHATY_PUPPET_SERVICE_TOKEN in the environment variables. 74 | # Learn more about services (and TOKEN) from https://wechaty.js.org/docs/puppet-services/ 75 | # 76 | # It is highly recommanded to use token like [paimon] and [wxwork]. 77 | # Those types of puppet_service are supported natively. 78 | # https://wechaty.js.org/docs/puppet-services/paimon 79 | # https://wechaty.js.org/docs/puppet-services/wxwork 80 | # 81 | # Replace your token here and umcommt that line, you can just run this python file successfully! 82 | # os.environ['token'] = 'puppet_paimon_your_token' 83 | # os.environ['token'] = 'puppet_wxwork_your_token' 84 | # 85 | if 'WECHATY_PUPPET_SERVICE_TOKEN' not in os.environ: 86 | print(''' 87 | Error: WECHATY_PUPPET_SERVICE_TOKEN is not found in the environment variables 88 | You need a TOKEN to run the Python Wechaty. Please goto our README for details 89 | https://github.com/wechaty/python-wechaty-getting-started/#wechaty_puppet_service_token 90 | ''') 91 | 92 | #for cloud-service 93 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='puppet_padlocal_3f904c6aa9cd4c548944f6688a829707' 94 | os.environ['WECHATY_PUPPET']='wechaty-puppet-padlocal' 95 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='43.154.97.162:8788' 96 | 97 | # export WECHATY_PUPPET=wechaty-puppet-padlocal 98 | # export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_3f904c6aa9cd4c548944f6688a829707 99 | # export WECHATY_PUPPET_SERVER_PORT=8788 100 | # export WECHATY_LOG=verbose 101 | 102 | #for local-service 103 | # os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='test01' 104 | # os.environ['WECHATY_PUPPET']='wechaty-puppet-xp' 105 | # os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='127.0.0.1:8080' 106 | 107 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER']='true' 108 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT']='true' 109 | 110 | bot = Wechaty() 111 | 112 | bot.on('scan', on_scan) 113 | bot.on('login', on_login) 114 | bot.on('message', on_message) 115 | 116 | await bot.start() 117 | 118 | print('[Python Wechaty] Ding Dong Bot started.') 119 | 120 | 121 | asyncio.run(main()) -------------------------------------------------------------------------------- /url_config.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import hashlib 3 | import time 4 | import json 5 | import os 6 | 7 | ACCOUNT = '' 8 | PHONE = '' 9 | 10 | # SUBMIT_URL = "http://api-air.inspur.com:32102/v1/interface/api/requestId?" 11 | # REPLY_URL = "http://api-air.inspur.com:32102/v1/interface/api/result?" 12 | 13 | SUBMIT_URL = "http://api-air.inspur.com:32102/v1/interface/api/infer/getRequestId?" 14 | REPLY_URL = "http://api-air.inspur.com:32102/v1/interface/api/result?" 15 | 16 | def code_md5(str): 17 | code=str.encode("utf-8") 18 | m = hashlib.md5() 19 | m.update(code) 20 | result= m.hexdigest() 21 | return result 22 | 23 | def rest_get(url, header,timeout, show_error=False): 24 | '''Call rest get method''' 25 | try: 26 | response = requests.get(url, headers=header,timeout=timeout, verify=False) 27 | return response 28 | except Exception as exception: 29 | if show_error: 30 | print(exception) 31 | return None 32 | 33 | def header_generation(): 34 | """Generate header for API request.""" 35 | t=time.strftime("%Y-%m-%d", time.localtime()) 36 | global ACCOUNT, PHONE 37 | ACCOUNT, PHONE = os.environ.get('YUAN_ACCOUNT').split('||') 38 | token=code_md5(ACCOUNT+PHONE+t) 39 | headers = {'token': token} 40 | return headers 41 | 42 | def submit_request(query,temperature,topP,topK,max_tokens,engine): 43 | """Submit query to the backend server and get requestID.""" 44 | headers=header_generation() 45 | # url=SUBMIT_URL + "account={0}&data={1}&temperature={2}&topP={3}&topK={4}&tokensToGenerate={5}&type={6}".format(ACCOUNT,query,temperature,topP,topK,max_tokens,"api") 46 | url=SUBMIT_URL + "engine={0}&account={1}&data={2}&temperature={3}&topP={4}&topK={5}&tokensToGenerate={6}" \ 47 | "&type={7}".format(engine,ACCOUNT,query,temperature,topP,topK, max_tokens,"api") 48 | #print(url) 49 | response=rest_get(url,headers,30) 50 | #print(response) 51 | response_text = json.loads(response.text) 52 | if response_text["flag"]: 53 | requestId = response_text["resData"] 54 | return requestId 55 | else: 56 | raise RuntimeWarning(response_text) 57 | 58 | def reply_request(requestId,cycle_count=5): 59 | """Check reply API to get the inference response.""" 60 | url = REPLY_URL + "account={0}&requestId={1}".format(ACCOUNT, requestId) 61 | headers=header_generation() 62 | for i in range(cycle_count): 63 | response = rest_get(url, headers, 30, show_error=True) 64 | response_text = json.loads(response.text) 65 | if response_text["resData"] != None: 66 | return response_text 67 | if response_text["flag"] == False and i ==cycle_count-1: 68 | raise RuntimeWarning(response_text) 69 | time.sleep(3) 70 | 71 | --------------------------------------------------------------------------------